game_machine 0.0.11 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (274) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -1
  3. data/Gemfile.lock +32 -47
  4. data/Rakefile +0 -27
  5. data/bin/bundle_run.sh +1 -0
  6. data/bin/game_machine +29 -27
  7. data/config/cluster.conf +6 -5
  8. data/config/default.conf +164 -0
  9. data/config/game_machine.sql +33 -0
  10. data/config/game_messages.proto +87 -25
  11. data/config/gamecloud.conf +140 -0
  12. data/config/messages.proto +46 -53
  13. data/config/test.conf +149 -0
  14. data/game_machine.gemspec +10 -5
  15. data/games/boot.rb +3 -0
  16. data/games/example/data/game_data.yml +4 -4
  17. data/games/example/lib/aggressive_npc.rb +1 -1
  18. data/games/example/lib/game.rb +1 -2
  19. data/games/example/lib/player_register.rb +1 -1
  20. data/games/routes.rb +9 -0
  21. data/games/tutorial/boot.rb +12 -0
  22. data/games/tutorial/item_manager.rb +256 -0
  23. data/games/tutorial/object_store.rb +55 -0
  24. data/games/tutorial/seed.rb +52 -0
  25. data/games/tutorial/sql_store.rb +30 -0
  26. data/java/project/build.gradle +134 -0
  27. data/java/project/component.erb +719 -0
  28. data/java/{gradle.properties → project/gradle.properties} +1 -1
  29. data/java/project/gradle/wrapper/gradle-wrapper.jar +0 -0
  30. data/java/{gradle → project/gradle}/wrapper/gradle-wrapper.properties +2 -2
  31. data/java/{gradlew → project/gradlew} +0 -0
  32. data/java/{gradlew.bat → project/gradlew.bat} +0 -0
  33. data/java/project/local_lib/AdminUi.jar +0 -0
  34. data/java/{local_lib/protostuff-compiler-1.0.7-jarjar.jar → project/local_lib/protostuff-compiler-1.0.8-jarjar.jar} +0 -0
  35. data/java/project/local_lib/sigar/libsigar-amd64-freebsd-6.so +0 -0
  36. data/java/project/local_lib/sigar/libsigar-amd64-linux.so +0 -0
  37. data/java/project/local_lib/sigar/libsigar-amd64-solaris.so +0 -0
  38. data/java/project/local_lib/sigar/libsigar-ia64-hpux-11.sl +0 -0
  39. data/java/project/local_lib/sigar/libsigar-ia64-linux.so +0 -0
  40. data/java/project/local_lib/sigar/libsigar-pa-hpux-11.sl +0 -0
  41. data/java/project/local_lib/sigar/libsigar-ppc-aix-5.so +0 -0
  42. data/java/project/local_lib/sigar/libsigar-ppc-linux.so +0 -0
  43. data/java/project/local_lib/sigar/libsigar-ppc64-aix-5.so +0 -0
  44. data/java/project/local_lib/sigar/libsigar-ppc64-linux.so +0 -0
  45. data/java/project/local_lib/sigar/libsigar-s390x-linux.so +0 -0
  46. data/java/project/local_lib/sigar/libsigar-sparc-solaris.so +0 -0
  47. data/java/project/local_lib/sigar/libsigar-sparc64-solaris.so +0 -0
  48. data/java/project/local_lib/sigar/libsigar-universal-macosx.dylib +0 -0
  49. data/java/project/local_lib/sigar/libsigar-universal64-macosx.dylib +0 -0
  50. data/java/project/local_lib/sigar/libsigar-x86-freebsd-5.so +0 -0
  51. data/java/project/local_lib/sigar/libsigar-x86-freebsd-6.so +0 -0
  52. data/java/project/local_lib/sigar/libsigar-x86-linux.so +0 -0
  53. data/java/project/local_lib/sigar/libsigar-x86-solaris.so +0 -0
  54. data/java/project/local_lib/sigar/sigar-amd64-winnt.dll +0 -0
  55. data/java/project/local_lib/sigar/sigar-x86-winnt.dll +0 -0
  56. data/java/project/local_lib/sigar/sigar-x86-winnt.lib +0 -0
  57. data/java/project/model.erb +99 -0
  58. data/java/{settings.gradle → project/settings.gradle} +0 -0
  59. data/java/project/src/main/java/com/game_machine/authentication/DefaultAuthenticator.java +28 -0
  60. data/java/project/src/main/java/com/game_machine/authentication/PlayerAuthenticator.java +6 -0
  61. data/java/project/src/main/java/com/game_machine/authentication/PublicAuthenticator.java +20 -0
  62. data/java/{src → project/src}/main/java/com/game_machine/core/ActorFactory.java +0 -0
  63. data/java/{src → project/src}/main/java/com/game_machine/core/ActorUtil.java +13 -0
  64. data/java/project/src/main/java/com/game_machine/core/AuthorizedPlayers.java +23 -0
  65. data/java/project/src/main/java/com/game_machine/core/ClientMessageDecoder.java +36 -0
  66. data/java/project/src/main/java/com/game_machine/core/ClientMessageEncoder.java +19 -0
  67. data/java/project/src/main/java/com/game_machine/core/CloudClient.java +298 -0
  68. data/java/{src → project/src}/main/java/com/game_machine/core/CommandProxy.java +0 -0
  69. data/java/project/src/main/java/com/game_machine/core/Commands.java +20 -0
  70. data/java/project/src/main/java/com/game_machine/core/DatastoreCommands.java +43 -0
  71. data/java/project/src/main/java/com/game_machine/core/DbConnectionPool.java +72 -0
  72. data/java/project/src/main/java/com/game_machine/core/DefaultMovementVerifier.java +56 -0
  73. data/java/{src → project/src}/main/java/com/game_machine/core/EntitySerializer.java +0 -0
  74. data/java/project/src/main/java/com/game_machine/core/EntityTracking.java +119 -0
  75. data/java/{src → project/src}/main/java/com/game_machine/core/EventStreamHandler.java +1 -1
  76. data/java/project/src/main/java/com/game_machine/core/GameActor.java +73 -0
  77. data/java/project/src/main/java/com/game_machine/core/GameMachineLoader.java +43 -0
  78. data/java/project/src/main/java/com/game_machine/core/GameMessageActor.java +44 -0
  79. data/java/project/src/main/java/com/game_machine/core/Grid.java +255 -0
  80. data/java/{src → project/src}/main/java/com/game_machine/core/GridValue.java +0 -0
  81. data/java/project/src/main/java/com/game_machine/core/Hashring.java +66 -0
  82. data/java/{src → project/src}/main/java/com/game_machine/core/IActorFactory.java +0 -0
  83. data/java/project/src/main/java/com/game_machine/core/LocalLinkedBuffer.java +20 -0
  84. data/java/project/src/main/java/com/game_machine/core/MessageGateway.java +120 -0
  85. data/java/project/src/main/java/com/game_machine/core/MessagePersister.java +26 -0
  86. data/java/project/src/main/java/com/game_machine/core/MonoProxy.java +39 -0
  87. data/java/project/src/main/java/com/game_machine/core/MovementVerifier.java +7 -0
  88. data/java/{src → project/src}/main/java/com/game_machine/core/NetMessage.java +10 -6
  89. data/java/project/src/main/java/com/game_machine/core/PersistentMessage.java +9 -0
  90. data/java/project/src/main/java/com/game_machine/core/PlayerCommands.java +31 -0
  91. data/java/project/src/main/java/com/game_machine/core/TcpServer.java +100 -0
  92. data/java/project/src/main/java/com/game_machine/core/TcpServerHandler.java +54 -0
  93. data/java/project/src/main/java/com/game_machine/core/TcpServerInitializer.java +32 -0
  94. data/java/project/src/main/java/com/game_machine/core/UdpClient.java +86 -0
  95. data/java/{src → project/src}/main/java/com/game_machine/core/UdpServer.java +18 -27
  96. data/java/{src → project/src}/main/java/com/game_machine/core/UdpServerHandler.java +23 -26
  97. data/java/project/src/main/java/com/game_machine/core/Vector3.java +159 -0
  98. data/java/project/src/main/java/com/game_machine/orm/models/PlayerItem.java +118 -0
  99. data/java/project/src/main/java/com/game_machine/orm/models/TestObject.java +110 -0
  100. data/java/project/src/main/java/com/game_machine/tutorial/LootGenerator.java +26 -0
  101. data/java/{src → project/src}/main/resources/game_machine.java.stg +3 -1
  102. data/java/project/src/main/resources/logback.properties +13 -0
  103. data/java/project/src/main/resources/logback.xml +76 -0
  104. data/java/{src → project/src}/main/resources/protostuff.properties +0 -0
  105. data/java/src/main/java/game/MyGameActor.java +26 -0
  106. data/lib/game_machine.rb +17 -16
  107. data/lib/game_machine/actor.rb +1 -1
  108. data/lib/game_machine/actor/base.rb +8 -31
  109. data/lib/game_machine/actor/builder.rb +5 -6
  110. data/lib/game_machine/actor/game_actor.rb +55 -0
  111. data/lib/game_machine/actor/reloadable.rb +6 -1
  112. data/lib/game_machine/akka.rb +26 -32
  113. data/lib/game_machine/app_config.rb +39 -26
  114. data/lib/game_machine/application.rb +56 -62
  115. data/lib/game_machine/client_manager.rb +14 -8
  116. data/lib/game_machine/cloud_updater.rb +51 -0
  117. data/lib/game_machine/cluster_monitor.rb +3 -3
  118. data/lib/game_machine/commands.rb +1 -1
  119. data/lib/game_machine/commands/misc_commands.rb +4 -8
  120. data/lib/game_machine/commands/player_commands.rb +8 -0
  121. data/lib/game_machine/console.rb +1 -0
  122. data/lib/game_machine/console/build.rb +57 -24
  123. data/lib/game_machine/console/bundle.rb +95 -0
  124. data/lib/game_machine/console/deploy.rb +30 -0
  125. data/lib/game_machine/console/install.rb +70 -36
  126. data/lib/game_machine/console/server.rb +2 -69
  127. data/lib/game_machine/data_store.rb +111 -15
  128. data/lib/game_machine/data_stores/couchbase.rb +8 -3
  129. data/lib/game_machine/data_stores/gamecloud.rb +93 -0
  130. data/lib/game_machine/data_stores/jdbc.rb +98 -0
  131. data/lib/game_machine/default_handlers.rb +2 -0
  132. data/lib/game_machine/default_handlers/team_handler.rb +51 -0
  133. data/lib/game_machine/default_handlers/zone_manager.rb +30 -0
  134. data/lib/game_machine/endpoints.rb +0 -4
  135. data/lib/game_machine/endpoints/udp_incoming.rb +13 -5
  136. data/lib/game_machine/endpoints/udp_outgoing.rb +15 -9
  137. data/lib/game_machine/game_systems.rb +0 -2
  138. data/lib/game_machine/game_systems/agents/controller.rb +2 -2
  139. data/lib/game_machine/game_systems/entity_tracking.rb +0 -3
  140. data/lib/game_machine/game_systems/region_manager.rb +3 -2
  141. data/lib/game_machine/game_systems/region_service.rb +2 -2
  142. data/lib/game_machine/game_systems/remote_echo.rb +10 -0
  143. data/lib/game_machine/game_systems/team_manager.rb +2 -11
  144. data/lib/game_machine/grid.rb +5 -18
  145. data/lib/game_machine/handlers/authentication.rb +1 -9
  146. data/lib/game_machine/handlers/game.rb +27 -2
  147. data/lib/game_machine/handlers/player_authentication.rb +87 -0
  148. data/lib/game_machine/handlers/request.rb +9 -11
  149. data/lib/game_machine/hocon_config.rb +81 -0
  150. data/lib/game_machine/java_lib.rb +14 -1
  151. data/lib/game_machine/logger.rb +10 -23
  152. data/lib/game_machine/models.rb +1 -0
  153. data/lib/game_machine/mono_server.rb +6 -1
  154. data/lib/game_machine/object_db.rb +12 -6
  155. data/lib/game_machine/protobuf.rb +1 -1
  156. data/lib/game_machine/protobuf/game_messages.rb +13 -3
  157. data/lib/game_machine/protobuf/generate.rb +107 -5
  158. data/lib/game_machine/restart_watcher.rb +1 -1
  159. data/lib/game_machine/routes.rb +23 -0
  160. data/lib/game_machine/scheduler.rb +1 -1
  161. data/lib/game_machine/securerandom.rb +2 -0
  162. data/lib/game_machine/system_stats.rb +28 -7
  163. data/lib/game_machine/version.rb +1 -1
  164. data/lib/game_machine/wavefront_ext.rb +47 -0
  165. data/lib/game_machine/write_behind_cache.rb +24 -9
  166. data/mono/server/Makefile +1 -1
  167. data/mono/server/Newtonsoft.Json.dll +0 -0
  168. data/mono/server/build.bat +1 -1
  169. data/mono/server/callable.cs +9 -0
  170. data/mono/server/echo.cs +17 -0
  171. data/mono/server/message_router.cs +16 -23
  172. data/mono/server/messages.cs +1792 -417
  173. data/mono/server/protobuf-net.dll +0 -0
  174. data/mono/server/server.cs +120 -0
  175. data/mono/server/server.exe +0 -0
  176. data/pathfinding/astar.cpp +149 -0
  177. data/pathfinding/build.sh +6 -0
  178. data/pathfinding/build.txt +16 -0
  179. data/pathfinding/crowd.cpp +194 -0
  180. data/pathfinding/include/astar.h +49 -0
  181. data/pathfinding/include/common.h +5 -0
  182. data/pathfinding/include/crowd.h +43 -0
  183. data/pathfinding/include/micropather.h +511 -0
  184. data/pathfinding/include/navmesh.h +114 -0
  185. data/pathfinding/include/pathfinder.h +24 -0
  186. data/pathfinding/main.cpp +108 -17
  187. data/pathfinding/micropather.cpp +1062 -0
  188. data/pathfinding/navmesh.cpp +408 -0
  189. data/pathfinding/overrides/DetourCrowd.cpp +1446 -0
  190. data/pathfinding/overrides/DetourNavMeshQuery.cpp +3551 -0
  191. data/pathfinding/overrides/DetourNavMeshQuery.h +538 -0
  192. data/pathfinding/pathfinder.cpp +117 -0
  193. data/pathfinding/{bin → premake}/premake4 +0 -0
  194. data/pathfinding/premake/premake4.exe +0 -0
  195. data/pathfinding/premake4.lua +12 -3
  196. data/spec/actor/actor_spec.rb +0 -7
  197. data/spec/client_manager_spec.rb +1 -1
  198. data/spec/couchproxy_spec.rb +38 -0
  199. data/spec/entity_persistence_spec.rb +129 -0
  200. data/spec/game_systems/team_manager_spec.rb +2 -2
  201. data/spec/hashring_spec.rb +17 -39
  202. data/spec/java_grid_spec.rb +0 -2
  203. data/spec/misc_spec.rb +111 -0
  204. data/spec/mono_spec.rb +50 -3
  205. data/spec/reliable_message_spec.rb +38 -0
  206. data/spec/spec_helper.rb +4 -4
  207. data/spec/spec_helper_minimal.rb +10 -0
  208. data/web/app.rb +108 -86
  209. data/web/config/trinidad.yml +1 -0
  210. data/web/views/add_player.erb +25 -0
  211. data/web/views/index.erb +0 -0
  212. data/web/views/layout.erb +48 -0
  213. data/web/views/login.erb +25 -0
  214. data/web/views/players.erb +24 -0
  215. metadata +209 -94
  216. data/config/config.example.yml +0 -100
  217. data/config/regions.example.yml +0 -9
  218. data/games/example/lib/authentication_handler.rb +0 -69
  219. data/games/models.rb +0 -3
  220. data/games/models/clan_member.rb +0 -8
  221. data/games/models/clan_profile.rb +0 -9
  222. data/games/models/player.rb +0 -7
  223. data/games/plugins.rb +0 -1
  224. data/games/plugins/team_handler.rb +0 -49
  225. data/games/preload.rb +0 -13
  226. data/java/.gitignore +0 -1
  227. data/java/build.gradle +0 -95
  228. data/java/component.erb +0 -396
  229. data/java/gradle/wrapper/gradle-wrapper.jar +0 -0
  230. data/java/src/main/java/com/game_machine/core/GameMachineLoader.java +0 -25
  231. data/java/src/main/java/com/game_machine/core/Grid.java +0 -195
  232. data/java/src/main/resources/logback.xml +0 -14
  233. data/java/src/main/resources/logging.properties +0 -3
  234. data/lib/game_machine/actor/mono_actor.rb +0 -89
  235. data/lib/game_machine/auth_handlers/base.rb +0 -21
  236. data/lib/game_machine/auth_handlers/public.rb +0 -34
  237. data/lib/game_machine/endpoints/mono_gateway.rb +0 -87
  238. data/lib/game_machine/endpoints/tcp.rb +0 -51
  239. data/lib/game_machine/endpoints/tcp_handler.rb +0 -75
  240. data/lib/game_machine/endpoints/udp.rb +0 -88
  241. data/lib/game_machine/game_loader.rb +0 -46
  242. data/lib/game_machine/game_systems/region_settings.rb +0 -13
  243. data/lib/game_machine/hashring.rb +0 -48
  244. data/lib/game_machine/settings.rb +0 -11
  245. data/mono/server/actor.cs +0 -37
  246. data/mono/server/iactor.cs +0 -11
  247. data/mono/server/message_util.cs +0 -29
  248. data/mono/server/proxy_client.cs +0 -73
  249. data/mono/server/proxy_server.cs +0 -30
  250. data/mono/server/test_actor.cs +0 -33
  251. data/pathfinding/include/pathfind.h +0 -167
  252. data/pathfinding/pathfind.cpp +0 -174
  253. data/pathfinding/pathfinder.cs +0 -66
  254. data/script/server.sh +0 -109
  255. data/script/watch.sh +0 -11
  256. data/spec/commands/navigation_commands_spec.rb +0 -51
  257. data/spec/game_systems/entity_tracking_spec.rb +0 -64
  258. data/spec/navigation/detour_navmesh_spec.rb +0 -34
  259. data/spec/navigation/detour_path_spec.rb +0 -25
  260. data/spec/udp_server_spec.rb +0 -10
  261. data/web/controllers/auth_controller.rb +0 -19
  262. data/web/controllers/base_controller.rb +0 -16
  263. data/web/controllers/index_controller.rb +0 -7
  264. data/web/controllers/log_controller.rb +0 -47
  265. data/web/controllers/messages_controller.rb +0 -59
  266. data/web/controllers/player_register_controller.rb +0 -15
  267. data/web/views/game_messages.haml +0 -45
  268. data/web/views/index.haml +0 -6
  269. data/web/views/layout.haml +0 -41
  270. data/web/views/logs.haml +0 -32
  271. data/web/views/player_register.haml +0 -22
  272. data/web/views/player_registered.haml +0 -2
  273. data/web/views/register_layout.haml +0 -22
  274. data/web/views/restart.haml +0 -35
@@ -0,0 +1,114 @@
1
+ #define _USE_MATH_DEFINES
2
+ #define DT_VIRTUAL_QUERYFILTER 1
3
+
4
+ #include <cmath>
5
+ #include <limits>
6
+ #include <math.h>
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #include <vector>
11
+ #include <math.h>
12
+ #include <map>
13
+ #include <float.h>
14
+ #include "Recast.h"
15
+ #include "DetourCommon.h"
16
+ #include "DetourNavMesh.h"
17
+ #include "DetourNavMeshBuilder.h"
18
+ #include "DetourNavMeshQuery.h"
19
+
20
+ #include "common.h"
21
+ #include "mesh_loader.h"
22
+
23
+ #define VERTEX_SIZE 3
24
+ #define INVALID_POLYREF 0
25
+
26
+ using namespace std;
27
+
28
+ class Navmesh {
29
+ int meshId;
30
+ dtNavMesh* mesh;
31
+ dtNavMeshQuery* query;
32
+
33
+ public:
34
+ int gwidth;
35
+ int gheight;
36
+ vector<int> gmap;
37
+
38
+
39
+ static const int P_FAILURE = -1;
40
+ static const int P_NO_START_POLY = -2;
41
+ static const int P_NO_END_POLY = -3;
42
+ static const int P_PATH_NOT_FOUND = -4;
43
+ static const int P_MESH_NOT_FOUND = -5;
44
+ static const int P_QUERY_NOT_FOUND = -6;
45
+
46
+
47
+ static const int MAX_POLYS = 512;
48
+ static const int MAX_STEER_POINTS = 10;
49
+ static const int MAX_SMOOTH = 2048;
50
+ static float const STEP_SIZE;
51
+ static float const SLOP;
52
+
53
+ enum SamplePolyFlags
54
+ {
55
+ SAMPLE_POLYFLAGS_WALK = 0x01, // Ability to walk (ground, grass, road)
56
+ SAMPLE_POLYFLAGS_SWIM = 0x02, // Ability to swim (water).
57
+ SAMPLE_POLYFLAGS_DOOR = 0x04, // Ability to move through doors.
58
+ SAMPLE_POLYFLAGS_JUMP = 0x08, // Ability to jump.
59
+ SAMPLE_POLYFLAGS_DISABLED = 0x10, // Disabled polygon
60
+ SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
61
+ };
62
+
63
+ Navmesh(int id, const char *file);
64
+ void gmapSetPassable(int a, int b, int radius);
65
+ void gmapSetBlocked(int a, int b, int radius);
66
+ dtNavMeshQuery* getQuery();
67
+ void EXPORT_API freeQuery(dtNavMeshQuery* query);
68
+ int findPath(float startx,
69
+ float starty, float startz, float endx, float endy, float endz,
70
+ int find_straight_path, float* resultPath);
71
+ float* getPathPtr(int max_paths);
72
+ void freePath(float* path);
73
+ dtNavMesh* getMesh();
74
+ bool meshLoaded();
75
+
76
+ int fixupCorridor(dtPolyRef* path, const int npath, const int maxPath,
77
+ const dtPolyRef* visited, const int nvisited);
78
+
79
+ bool getSteerTarget(dtNavMeshQuery* navQuery, const float* startPos, const float* endPos,
80
+ const float minTargetDist,
81
+ const dtPolyRef* path, const int pathSize,
82
+ float* steerPos, unsigned char& steerPosFlag, dtPolyRef& steerPosRef,
83
+ float* outPoints = 0, int* outPointCount = 0);
84
+
85
+ int fixupShortcuts(dtPolyRef* path, int npath, dtNavMeshQuery* navQuery);
86
+ inline bool inRange(const float* v1, const float* v2, const float r, const float h);
87
+ };
88
+
89
+ class myQueryFilter : public dtQueryFilter {
90
+
91
+ private:
92
+ float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
93
+ unsigned short m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
94
+ unsigned short m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.)
95
+ Navmesh* navmesh;
96
+ public:
97
+ myQueryFilter();
98
+ ~myQueryFilter();
99
+
100
+ void setNavmesh(Navmesh* n);
101
+ bool isPassable(const float* pa, const float* pb) const;
102
+ bool passFilter(const dtPolyRef /*ref*/,
103
+ const dtMeshTile* /*tile*/,
104
+ const dtPoly* poly) const;
105
+
106
+
107
+ float getCost(const float* pa, const float* pb,
108
+ const dtPolyRef /*prevRef*/, const dtMeshTile* /*prevTile*/, const dtPoly* /*prevPoly*/,
109
+ const dtPolyRef /*curRef*/, const dtMeshTile* /*curTile*/, const dtPoly* curPoly,
110
+ const dtPolyRef /*nextRef*/, const dtMeshTile* /*nextTile*/, const dtPoly* /*nextPoly*/) const;
111
+
112
+
113
+ };
114
+
@@ -0,0 +1,24 @@
1
+
2
+ #include "crowd.h"
3
+ #include "astar.h"
4
+
5
+ extern "C" EXPORT_API void addPathfinder(int i, int width, int height);
6
+ extern "C" EXPORT_API void setPassable(int pathfinder, int x, int y);
7
+ extern "C" EXPORT_API void setBlocked(int pathfinder, int x, int y);
8
+ extern "C" EXPORT_API int getXAt(int pathfinder, int index);
9
+ extern "C" EXPORT_API int getYAt(int pathfinder, int index);
10
+ extern "C" EXPORT_API int findpath2d(int pathfinder, int startx, int starty, int endx, int endy);
11
+
12
+ extern "C" EXPORT_API void addCrowd(int id, int navmeshid);
13
+ extern "C" EXPORT_API int addAgent(int crowdId, const float* p, float accel, float speed, float radius,
14
+ float height, int optflag, float sepWeight);
15
+ extern "C" EXPORT_API void removeAgent(int crowdId, const int idx);
16
+ extern "C" EXPORT_API void setMoveTarget(int crowdId, const float* p, bool adjust, int agentIdx);
17
+ extern "C" EXPORT_API void updateTick(int crowdId, const float dt);
18
+ extern "C" EXPORT_API void getAgentPosition(int crowdId, int idx, float* resultPath);
19
+
20
+ extern "C" EXPORT_API void addNavmesh(int id, const char *file);
21
+ extern "C" EXPORT_API void gmapSetPassable(int navmeshId, int a, int b, int radius);
22
+ extern "C" EXPORT_API void gmapSetBlocked(int navmeshId, int a, int b, int radius);
23
+ extern "C" EXPORT_API int findPath(int navmeshId, float startx, float starty,
24
+ float startz, float endx, float endy, float endz, int find_straight_path, float* resultPath);
@@ -1,39 +1,130 @@
1
- #include "pathfind.h"
1
+ #if _MSC_VER
2
+ #else
3
+ #include "pathfinder.h"
4
+ #include <cstdlib>
5
+ #include <stdio.h>
6
+ #include <float.h>
7
+ #include <time.h>
8
+ #include <stdint.h>
2
9
 
3
- int main (int argc, char* argv[]) {
4
10
 
5
- int find_straight_path = 1;
6
- float *newPath;
7
- newPath = getPathPtr(MAX_SMOOTH);
11
+ #include <sys/time.h>
12
+ #include <ctime>
13
+ using namespace std;
14
+
15
+ typedef unsigned long long timestamp_t;
8
16
 
9
- //const char *file = "/home2/chris/game_machine/server/detour/meshes/terrain.bin";
10
- const char *file = "/home2/chris/game_machine/data/meshes/all_tiles_navmesh.bin";
17
+ static timestamp_t get_timestamp ()
18
+ {
19
+ struct timeval now;
20
+ gettimeofday (&now, NULL);
21
+ return now.tv_usec + (timestamp_t)now.tv_sec * 1000000;
22
+ }
11
23
 
24
+ void testnavmesh() {
25
+ const char *file = "/home/chris/game_machine/server/pathfinding/all_tiles_navmesh.bin";
26
+ Navmesh* navmesh = new Navmesh(1,file);
12
27
 
13
- int loadRes = loadNavMesh(1,file);
14
- fprintf (stderr, "loadNavMesh returned %d\n", loadRes);
15
- dtNavMeshQuery* query = getQuery(1);
28
+ int find_straight_path = 1;
29
+ float *newPath;
30
+ newPath = navmesh->getPathPtr(Navmesh::MAX_SMOOTH);
31
+ timestamp_t t0;
32
+ timestamp_t t1;
33
+ double secs;
16
34
 
17
- if (loadRes == 1) {
35
+ t0 = get_timestamp();
36
+ for (int j = 0; j < 15; ++j) {
37
+ navmesh->gmapSetPassable(200,200,3);
38
+ navmesh->gmapSetBlocked(130,95,3);
39
+ }
40
+ t1 = get_timestamp();
41
+ secs = (t1 - t0) / 1000000.0L;
42
+ fprintf (stderr, "setPassable %f\n", secs);
43
+
44
+ if (navmesh->meshLoaded()) {
18
45
  for (int j = 0; j < 1; ++j) {
19
46
  for (int i = 0; i < 1; ++i) {
20
- //int res = findPath(query, 563.0, 0.2, 504.0, 509.0, 0.2, 528.0,
21
- int res = findPath(query, 563.0f, 0.2f, 504.0f, 563.0f, 0.2f, 541.0f,
47
+ t0 = get_timestamp();
48
+ int res = navmesh->findPath(10.0f, 0.2f, 10.0f, 300.0f, 0.2f, 300.0f,
22
49
  find_straight_path, newPath);
50
+ t1 = get_timestamp();
51
+ secs = (t1 - t0) / 1000000.0L;
52
+ fprintf (stderr, "end time %f\n", secs);
53
+
23
54
  fprintf (stderr, "findPath returned %d\n", res);
24
55
  for (int i = 0; i < res; ++i) {
25
56
  //fprintf (stderr, "%d\n", i);
26
57
  const float* v = &newPath[i*3];
27
- fprintf (stderr, "%f.%f.%f\n", v[0], v[1], v[2]);
58
+ fprintf (stderr, "%f %f %f\n", v[0], v[1], v[2]);
28
59
  }
29
60
  }
30
61
  }
31
62
  }
63
+
64
+
32
65
 
33
66
  fprintf (stderr, "endLoop\n");
34
- freePath(newPath);
67
+ navmesh->freePath(newPath);
35
68
  fprintf (stderr, "freePath\n");
36
- freeQuery(query);
37
- fprintf (stderr, "freeQuery\n");
69
+
70
+
71
+
72
+
73
+ Crowd* crowd = new Crowd(navmesh);
74
+ float delta = 1.01f;
75
+ float agentPos[3] = {10.0f,0.0f,10.0f};
76
+ float targetPos[3] = {300.0f,0.0f,300.0f};
77
+ const float* ap = &agentPos[0];
78
+ const float* tp = &targetPos[0];
79
+ fprintf (stderr, "createCrowd\n");
80
+
81
+
82
+ crowd->setMoveTarget(tp, false, -1);
83
+ fprintf (stderr, "setMoveTarget\n");
84
+
85
+ for (int j = 0; j < 100; ++j) {
86
+ crowd->addAgent(ap, 8.0f, 3.5f, 0.6f, 2.0f,5,2.0f);
87
+ }
88
+
89
+ fprintf (stderr, "addAgent\n");
90
+
91
+ for (int j = 0; j < 900; ++j) {
92
+ crowd->updateTick(delta);
93
+ for (int i = 0; i < crowd->getCrowd()->getAgentCount(); ++i)
94
+ {
95
+ const dtCrowdAgent* ag = crowd->getCrowd()->getAgent(i);
96
+ if (!ag->active)
97
+ continue;
98
+ const float* v = ag->npos;
99
+ fprintf (stderr, "%d = %f %f %f\n", i, v[0], v[1], v[2]);
100
+ }
101
+
102
+ }
103
+ fprintf (stderr, "updateTick\n");
104
+
105
+
106
+ }
107
+
108
+ #endif
109
+
110
+ int main (int argc, char* argv[]) {
111
+ #if _MSC_VER
112
+ #else
113
+ testnavmesh();
114
+ return 1;
115
+
116
+ addPathfinder(1,500,500);
117
+ for (int j = 0; j < 500; ++j) {
118
+ for (int i = 0; i < 500; ++i) {
119
+ //setPassable(1,j,i);
120
+ }
121
+ }
122
+ fprintf (stderr, "grid created\n");
123
+
124
+ int res = findpath2d(1,0,0,100,100);
125
+ fprintf (stderr, "findpath2d returned %d\n", res);
126
+
127
+ #endif
128
+
38
129
  return 1;
39
130
  }
@@ -0,0 +1,1062 @@
1
+ /*
2
+ Copyright (c) 2000-2009 Lee Thomason (www.grinninglizard.com)
3
+
4
+ Grinning Lizard Utilities.
5
+
6
+ This software is provided 'as-is', without any express or implied
7
+ warranty. In no event will the authors be held liable for any
8
+ damages arising from the use of this software.
9
+
10
+ Permission is granted to anyone to use this software for any
11
+ purpose, including commercial applications, and to alter it and
12
+ redistribute it freely, subject to the following restrictions:
13
+
14
+ 1. The origin of this software must not be misrepresented; you must
15
+ not claim that you wrote the original software. If you use this
16
+ software in a product, an acknowledgment in the product documentation
17
+ would be appreciated but is not required.
18
+
19
+ 2. Altered source versions must be plainly marked as such, and
20
+ must not be misrepresented as being the original software.
21
+
22
+ 3. This notice may not be removed or altered from any source
23
+ distribution.
24
+ */
25
+
26
+ #ifdef _MSC_VER
27
+ #pragma warning( disable : 4786 ) // Debugger truncating names.
28
+ #pragma warning( disable : 4530 ) // Exception handler isn't used
29
+ #endif
30
+
31
+ //#include <vector>
32
+ #include <memory.h>
33
+ #include <stdio.h>
34
+
35
+ //#define DEBUG_PATH
36
+ //#define DEBUG_PATH_DEEP
37
+ //#define TRACK_COLLISION
38
+ //#define DEBUG_CACHING
39
+
40
+ #ifdef DEBUG_CACHING
41
+ #include "../grinliz/gldebug.h"
42
+ #endif
43
+
44
+ #include "micropather.h"
45
+
46
+ using namespace std;
47
+ using namespace micropather;
48
+
49
+ class OpenQueue
50
+ {
51
+ public:
52
+ OpenQueue( Graph* _graph )
53
+ {
54
+ graph = _graph;
55
+ sentinel = (PathNode*) sentinelMem;
56
+ sentinel->InitSentinel();
57
+ #ifdef DEBUG
58
+ sentinel->CheckList();
59
+ #endif
60
+ }
61
+ ~OpenQueue() {}
62
+
63
+ void Push( PathNode* pNode );
64
+ PathNode* Pop();
65
+ void Update( PathNode* pNode );
66
+
67
+ bool Empty() { return sentinel->next == sentinel; }
68
+
69
+ private:
70
+ OpenQueue( const OpenQueue& ); // undefined and unsupported
71
+ void operator=( const OpenQueue& );
72
+
73
+ PathNode* sentinel;
74
+ int sentinelMem[ ( sizeof( PathNode ) + sizeof( int ) ) / sizeof( int ) ];
75
+ Graph* graph; // for debugging
76
+ };
77
+
78
+
79
+ void OpenQueue::Push( PathNode* pNode )
80
+ {
81
+
82
+ MPASSERT( pNode->inOpen == 0 );
83
+ MPASSERT( pNode->inClosed == 0 );
84
+
85
+ #ifdef DEBUG_PATH_DEEP
86
+ printf( "Open Push: " );
87
+ graph->PrintStateInfo( pNode->state );
88
+ printf( " total=%.1f\n", pNode->totalCost );
89
+ #endif
90
+
91
+ // Add sorted. Lowest to highest cost path. Note that the sentinel has
92
+ // a value of FLT_MAX, so it should always be sorted in.
93
+ MPASSERT( pNode->totalCost < FLT_MAX );
94
+ PathNode* iter = sentinel->next;
95
+ while ( true )
96
+ {
97
+
98
+ if ( pNode->totalCost < iter->totalCost ) {
99
+ iter->AddBefore( pNode );
100
+ pNode->inOpen = 1;
101
+ break;
102
+ }
103
+ iter = iter->next;
104
+ }
105
+ MPASSERT( pNode->inOpen ); // make sure this was actually added.
106
+ #ifdef DEBUG
107
+ sentinel->CheckList();
108
+ #endif
109
+ }
110
+
111
+ PathNode* OpenQueue::Pop()
112
+ {
113
+ MPASSERT( sentinel->next != sentinel );
114
+ PathNode* pNode = sentinel->next;
115
+ pNode->Unlink();
116
+ #ifdef DEBUG
117
+ sentinel->CheckList();
118
+ #endif
119
+
120
+ MPASSERT( pNode->inClosed == 0 );
121
+ MPASSERT( pNode->inOpen == 1 );
122
+ pNode->inOpen = 0;
123
+
124
+ #ifdef DEBUG_PATH_DEEP
125
+ printf( "Open Pop: " );
126
+ graph->PrintStateInfo( pNode->state );
127
+ printf( " total=%.1f\n", pNode->totalCost );
128
+ #endif
129
+
130
+ return pNode;
131
+ }
132
+
133
+ void OpenQueue::Update( PathNode* pNode )
134
+ {
135
+ #ifdef DEBUG_PATH_DEEP
136
+ printf( "Open Update: " );
137
+ graph->PrintStateInfo( pNode->state );
138
+ printf( " total=%.1f\n", pNode->totalCost );
139
+ #endif
140
+
141
+ MPASSERT( pNode->inOpen );
142
+
143
+ // If the node now cost less than the one before it,
144
+ // move it to the front of the list.
145
+ if ( pNode->prev != sentinel && pNode->totalCost < pNode->prev->totalCost ) {
146
+ pNode->Unlink();
147
+ sentinel->next->AddBefore( pNode );
148
+ }
149
+
150
+ // If the node is too high, move to the right.
151
+ if ( pNode->totalCost > pNode->next->totalCost ) {
152
+ PathNode* it = pNode->next;
153
+ pNode->Unlink();
154
+
155
+ while ( pNode->totalCost > it->totalCost )
156
+ it = it->next;
157
+
158
+ it->AddBefore( pNode );
159
+ #ifdef DEBUG
160
+ sentinel->CheckList();
161
+ #endif
162
+ }
163
+ }
164
+
165
+
166
+ class ClosedSet
167
+ {
168
+ public:
169
+ ClosedSet( Graph* _graph ) { this->graph = _graph; }
170
+ ~ClosedSet() {}
171
+
172
+ void Add( PathNode* pNode )
173
+ {
174
+ #ifdef DEBUG_PATH_DEEP
175
+ printf( "Closed add: " );
176
+ graph->PrintStateInfo( pNode->state );
177
+ printf( " total=%.1f\n", pNode->totalCost );
178
+ #endif
179
+ #ifdef DEBUG
180
+ MPASSERT( pNode->inClosed == 0 );
181
+ MPASSERT( pNode->inOpen == 0 );
182
+ #endif
183
+ pNode->inClosed = 1;
184
+ }
185
+
186
+ void Remove( PathNode* pNode )
187
+ {
188
+ #ifdef DEBUG_PATH_DEEP
189
+ printf( "Closed remove: " );
190
+ graph->PrintStateInfo( pNode->state );
191
+ printf( " total=%.1f\n", pNode->totalCost );
192
+ #endif
193
+ MPASSERT( pNode->inClosed == 1 );
194
+ MPASSERT( pNode->inOpen == 0 );
195
+
196
+ pNode->inClosed = 0;
197
+ }
198
+
199
+ private:
200
+ ClosedSet( const ClosedSet& );
201
+ void operator=( const ClosedSet& );
202
+ Graph* graph;
203
+ };
204
+
205
+
206
+ PathNodePool::PathNodePool( unsigned _allocate, unsigned _typicalAdjacent )
207
+ : firstBlock( 0 ),
208
+ blocks( 0 ),
209
+ #if defined( MICROPATHER_STRESS )
210
+ allocate( 32 ),
211
+ #else
212
+ allocate( _allocate ),
213
+ #endif
214
+ nAllocated( 0 ),
215
+ nAvailable( 0 )
216
+ {
217
+ freeMemSentinel.InitSentinel();
218
+
219
+ cacheCap = allocate * _typicalAdjacent;
220
+ cacheSize = 0;
221
+ cache = (NodeCost*)malloc(cacheCap * sizeof(NodeCost));
222
+
223
+ // Want the behavior that if the actual number of states is specified, the cache
224
+ // will be at least that big.
225
+ hashShift = 3; // 8 (only useful for stress testing)
226
+ #if !defined( MICROPATHER_STRESS )
227
+ while( HashSize() < allocate )
228
+ ++hashShift;
229
+ #endif
230
+ hashTable = (PathNode**)calloc( HashSize(), sizeof(PathNode*) );
231
+
232
+ blocks = firstBlock = NewBlock();
233
+ // printf( "HashSize=%d allocate=%d\n", HashSize(), allocate );
234
+ totalCollide = 0;
235
+ }
236
+
237
+
238
+ PathNodePool::~PathNodePool()
239
+ {
240
+ Clear();
241
+ free( firstBlock );
242
+ free( cache );
243
+ free( hashTable );
244
+ #ifdef TRACK_COLLISION
245
+ printf( "Total collide=%d HashSize=%d HashShift=%d\n", totalCollide, HashSize(), hashShift );
246
+ #endif
247
+ }
248
+
249
+
250
+ bool PathNodePool::PushCache( const NodeCost* nodes, int nNodes, int* start ) {
251
+ *start = -1;
252
+ if ( nNodes+cacheSize <= cacheCap ) {
253
+ for( int i=0; i<nNodes; ++i ) {
254
+ cache[i+cacheSize] = nodes[i];
255
+ }
256
+ *start = cacheSize;
257
+ cacheSize += nNodes;
258
+ return true;
259
+ }
260
+ return false;
261
+ }
262
+
263
+
264
+ void PathNodePool::Clear()
265
+ {
266
+ #ifdef TRACK_COLLISION
267
+ // Collision tracking code.
268
+ int collide=0;
269
+ for( unsigned i=0; i<HashSize(); ++i ) {
270
+ if ( hashTable[i] && (hashTable[i]->child[0] || hashTable[i]->child[1]) )
271
+ ++collide;
272
+ }
273
+ //printf( "PathNodePool %d/%d collision=%d %.1f%%\n", nAllocated, HashSize(), collide, 100.0f*(float)collide/(float)HashSize() );
274
+ totalCollide += collide;
275
+ #endif
276
+
277
+ Block* b = blocks;
278
+ while( b ) {
279
+ Block* temp = b->nextBlock;
280
+ if ( b != firstBlock ) {
281
+ free( b );
282
+ }
283
+ b = temp;
284
+ }
285
+ blocks = firstBlock; // Don't delete the first block (we always need at least that much memory.)
286
+
287
+ // Set up for new allocations (but don't do work we don't need to. Reset/Clear can be called frequently.)
288
+ if ( nAllocated > 0 ) {
289
+ freeMemSentinel.next = &freeMemSentinel;
290
+ freeMemSentinel.prev = &freeMemSentinel;
291
+
292
+ memset( hashTable, 0, sizeof(PathNode*)*HashSize() );
293
+ for( unsigned i=0; i<allocate; ++i ) {
294
+ freeMemSentinel.AddBefore( &firstBlock->pathNode[i] );
295
+ }
296
+ }
297
+ nAvailable = allocate;
298
+ nAllocated = 0;
299
+ cacheSize = 0;
300
+ }
301
+
302
+
303
+ PathNodePool::Block* PathNodePool::NewBlock()
304
+ {
305
+ Block* block = (Block*) calloc( 1, sizeof(Block) + sizeof(PathNode)*(allocate-1) );
306
+ block->nextBlock = 0;
307
+
308
+ nAvailable += allocate;
309
+ for( unsigned i=0; i<allocate; ++i ) {
310
+ freeMemSentinel.AddBefore( &block->pathNode[i] );
311
+ }
312
+ return block;
313
+ }
314
+
315
+
316
+ unsigned PathNodePool::Hash( void* voidval )
317
+ {
318
+ /*
319
+ Spent quite some time on this, and the result isn't quite satifactory. The
320
+ input set is the size of a void*, and is generally (x,y) pairs or memory pointers.
321
+
322
+ FNV resulting in about 45k collisions in a (large) test and some other approaches
323
+ about the same.
324
+
325
+ Simple folding reduces collisions to about 38k - big improvement. However, that may
326
+ be an artifact of the (x,y) pairs being well distributed. And for either the x,y case
327
+ or the pointer case, there are probably very poor hash table sizes that cause "overlaps"
328
+ and grouping. (An x,y encoding with a hashShift of 8 is begging for trouble.)
329
+
330
+ The best tested results are simple folding, but that seems to beg for a pathelogical case.
331
+ FNV-1a was the next best choice, without obvious pathelogical holes.
332
+
333
+ Finally settled on h%HashMask(). Simple, but doesn't have the obvious collision cases of folding.
334
+ */
335
+ /*
336
+ // Time: 567
337
+ // FNV-1a
338
+ // http://isthe.com/chongo/tech/comp/fnv/
339
+ // public domain.
340
+ MP_UPTR val = (MP_UPTR)(voidval);
341
+ const unsigned char *p = (unsigned char *)(&val);
342
+ unsigned int h = 2166136261;
343
+
344
+ for( size_t i=0; i<sizeof(MP_UPTR); ++i, ++p ) {
345
+ h ^= *p;
346
+ h *= 16777619;
347
+ }
348
+ // Fold the high bits to the low bits. Doesn't (generally) use all
349
+ // the bits since the shift is usually < 16, but better than not
350
+ // using the high bits at all.
351
+ return ( h ^ (h>>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask();
352
+ */
353
+ /*
354
+ // Time: 526
355
+ MP_UPTR h = (MP_UPTR)(voidval);
356
+ return ( h ^ (h>>hashShift) ^ (h>>(hashShift*2)) ^ (h>>(hashShift*3)) ) & HashMask();
357
+ */
358
+
359
+ // Time: 512
360
+ // The HashMask() is used as the divisor. h%1024 has lots of common
361
+ // repetitions, but h%1023 will move things out more.
362
+ MP_UPTR h = (MP_UPTR)(voidval);
363
+ return h % HashMask();
364
+ }
365
+
366
+
367
+
368
+ PathNode* PathNodePool::Alloc()
369
+ {
370
+ if ( freeMemSentinel.next == &freeMemSentinel ) {
371
+ MPASSERT( nAvailable == 0 );
372
+
373
+ Block* b = NewBlock();
374
+ b->nextBlock = blocks;
375
+ blocks = b;
376
+ MPASSERT( freeMemSentinel.next != &freeMemSentinel );
377
+ }
378
+ PathNode* pathNode = freeMemSentinel.next;
379
+ pathNode->Unlink();
380
+
381
+ ++nAllocated;
382
+ MPASSERT( nAvailable > 0 );
383
+ --nAvailable;
384
+ return pathNode;
385
+ }
386
+
387
+
388
+ void PathNodePool::AddPathNode( unsigned key, PathNode* root )
389
+ {
390
+ if ( hashTable[key] ) {
391
+ PathNode* p = hashTable[key];
392
+ while( true ) {
393
+ int dir = (root->state < p->state) ? 0 : 1;
394
+ if ( p->child[dir] ) {
395
+ p = p->child[dir];
396
+ }
397
+ else {
398
+ p->child[dir] = root;
399
+ break;
400
+ }
401
+ }
402
+ }
403
+ else {
404
+ hashTable[key] = root;
405
+ }
406
+ }
407
+
408
+
409
+ PathNode* PathNodePool::FetchPathNode( void* state )
410
+ {
411
+ unsigned key = Hash( state );
412
+
413
+ PathNode* root = hashTable[key];
414
+ while( root ) {
415
+ if ( root->state == state ) {
416
+ break;
417
+ }
418
+ root = ( state < root->state ) ? root->child[0] : root->child[1];
419
+ }
420
+ MPASSERT( root );
421
+ return root;
422
+ }
423
+
424
+
425
+ PathNode* PathNodePool::GetPathNode( unsigned frame, void* _state, float _costFromStart, float _estToGoal, PathNode* _parent )
426
+ {
427
+ unsigned key = Hash( _state );
428
+
429
+ PathNode* root = hashTable[key];
430
+ while( root ) {
431
+ if ( root->state == _state ) {
432
+ if ( root->frame == frame ) // This is the correct state and correct frame.
433
+ break;
434
+ // Correct state, wrong frame.
435
+ root->Init( frame, _state, _costFromStart, _estToGoal, _parent );
436
+ break;
437
+ }
438
+ root = ( _state < root->state ) ? root->child[0] : root->child[1];
439
+ }
440
+ if ( !root ) {
441
+ // allocate new one
442
+ root = Alloc();
443
+ root->Clear();
444
+ root->Init( frame, _state, _costFromStart, _estToGoal, _parent );
445
+ AddPathNode( key, root );
446
+ }
447
+ return root;
448
+ }
449
+
450
+
451
+ void PathNode::Init( unsigned _frame,
452
+ void* _state,
453
+ float _costFromStart,
454
+ float _estToGoal,
455
+ PathNode* _parent )
456
+ {
457
+ state = _state;
458
+ costFromStart = _costFromStart;
459
+ estToGoal = _estToGoal;
460
+ CalcTotalCost();
461
+ parent = _parent;
462
+ frame = _frame;
463
+ inOpen = 0;
464
+ inClosed = 0;
465
+ }
466
+
467
+ MicroPather::MicroPather( Graph* _graph, unsigned allocate, unsigned typicalAdjacent, bool cache )
468
+ : pathNodePool( allocate, typicalAdjacent ),
469
+ graph( _graph ),
470
+ frame( 0 )
471
+ {
472
+ MPASSERT( allocate );
473
+ MPASSERT( typicalAdjacent );
474
+ pathCache = 0;
475
+ if ( cache ) {
476
+ pathCache = new PathCache( allocate*4 ); // untuned arbitrary constant
477
+ }
478
+ }
479
+
480
+
481
+ MicroPather::~MicroPather()
482
+ {
483
+ delete pathCache;
484
+ }
485
+
486
+
487
+ void MicroPather::Reset()
488
+ {
489
+ pathNodePool.Clear();
490
+ if ( pathCache ) {
491
+ pathCache->Reset();
492
+ }
493
+ frame = 0;
494
+ }
495
+
496
+
497
+ void MicroPather::GoalReached( PathNode* node, void* start, void* end, MP_VECTOR< void* > *_path )
498
+ {
499
+ MP_VECTOR< void* >& path = *_path;
500
+ path.clear();
501
+
502
+ // We have reached the goal.
503
+ // How long is the path? Used to allocate the vector which is returned.
504
+ int count = 1;
505
+ PathNode* it = node;
506
+ while( it->parent )
507
+ {
508
+ ++count;
509
+ it = it->parent;
510
+ }
511
+
512
+ // Now that the path has a known length, allocate
513
+ // and fill the vector that will be returned.
514
+ if ( count < 3 )
515
+ {
516
+ // Handle the short, special case.
517
+ path.resize(2);
518
+ path[0] = start;
519
+ path[1] = end;
520
+ }
521
+ else
522
+ {
523
+ path.resize(count);
524
+
525
+ path[0] = start;
526
+ path[count-1] = end;
527
+ count-=2;
528
+ it = node->parent;
529
+
530
+ while ( it->parent )
531
+ {
532
+ path[count] = it->state;
533
+ it = it->parent;
534
+ --count;
535
+ }
536
+ }
537
+
538
+ if ( pathCache ) {
539
+ costVec.clear();
540
+
541
+ PathNode* pn0 = pathNodePool.FetchPathNode( path[0] );
542
+ PathNode* pn1 = 0;
543
+ for( unsigned i=0; i<path.size()-1; ++i ) {
544
+ pn1 = pathNodePool.FetchPathNode( path[i+1] );
545
+ nodeCostVec.clear();
546
+ GetNodeNeighbors( pn0, &nodeCostVec );
547
+ for( unsigned j=0; j<nodeCostVec.size(); ++j ) {
548
+ if ( nodeCostVec[j].node == pn1 ) {
549
+ costVec.push_back( nodeCostVec[j].cost );
550
+ break;
551
+ }
552
+ }
553
+ MPASSERT( costVec.size() == i+1 );
554
+ pn0 = pn1;
555
+ }
556
+ pathCache->Add( path, costVec );
557
+ }
558
+
559
+ #ifdef DEBUG_PATH
560
+ printf( "Path: " );
561
+ int counter=0;
562
+ #endif
563
+ for ( unsigned k=0; k<path.size(); ++k )
564
+ {
565
+ #ifdef DEBUG_PATH
566
+ graph->PrintStateInfo( path[k] );
567
+ printf( " " );
568
+ ++counter;
569
+ if ( counter == 8 )
570
+ {
571
+ printf( "\n" );
572
+ counter = 0;
573
+ }
574
+ #endif
575
+ }
576
+ #ifdef DEBUG_PATH
577
+ printf( "Cost=%.1f Checksum %d\n", node->costFromStart, checksum );
578
+ #endif
579
+ }
580
+
581
+
582
+ void MicroPather::GetNodeNeighbors( PathNode* node, MP_VECTOR< NodeCost >* pNodeCost )
583
+ {
584
+ if ( node->numAdjacent == 0 ) {
585
+ // it has no neighbors.
586
+ pNodeCost->resize( 0 );
587
+ }
588
+ else if ( node->cacheIndex < 0 )
589
+ {
590
+ // Not in the cache. Either the first time or just didn't fit. We don't know
591
+ // the number of neighbors and need to call back to the client.
592
+ stateCostVec.resize( 0 );
593
+ graph->AdjacentCost( node->state, &stateCostVec );
594
+
595
+ #ifdef DEBUG
596
+ {
597
+ // If this assert fires, you have passed a state
598
+ // as its own neighbor state. This is impossible --
599
+ // bad things will happen.
600
+ for ( unsigned i=0; i<stateCostVec.size(); ++i )
601
+ MPASSERT( stateCostVec[i].state != node->state );
602
+ }
603
+ #endif
604
+
605
+ pNodeCost->resize( stateCostVec.size() );
606
+ node->numAdjacent = stateCostVec.size();
607
+
608
+ if ( node->numAdjacent > 0 ) {
609
+ // Now convert to pathNodes.
610
+ // Note that the microsoft std library is actually pretty slow.
611
+ // Move things to temp vars to help.
612
+ const unsigned stateCostVecSize = stateCostVec.size();
613
+ const StateCost* stateCostVecPtr = &stateCostVec[0];
614
+ NodeCost* pNodeCostPtr = &(*pNodeCost)[0];
615
+
616
+ for( unsigned i=0; i<stateCostVecSize; ++i ) {
617
+ void* state = stateCostVecPtr[i].state;
618
+ pNodeCostPtr[i].cost = stateCostVecPtr[i].cost;
619
+ pNodeCostPtr[i].node = pathNodePool.GetPathNode( frame, state, FLT_MAX, FLT_MAX, 0 );
620
+ }
621
+
622
+ // Can this be cached?
623
+ int start = 0;
624
+ if ( pNodeCost->size() > 0 && pathNodePool.PushCache( pNodeCostPtr, pNodeCost->size(), &start ) ) {
625
+ node->cacheIndex = start;
626
+ }
627
+ }
628
+ }
629
+ else {
630
+ // In the cache!
631
+ pNodeCost->resize( node->numAdjacent );
632
+ NodeCost* pNodeCostPtr = &(*pNodeCost)[0];
633
+ pathNodePool.GetCache( node->cacheIndex, node->numAdjacent, pNodeCostPtr );
634
+
635
+ // A node is uninitialized (even if memory is allocated) if it is from a previous frame.
636
+ // Check for that, and Init() as necessary.
637
+ for( int i=0; i<node->numAdjacent; ++i ) {
638
+ PathNode* pNode = pNodeCostPtr[i].node;
639
+ if ( pNode->frame != frame ) {
640
+ pNode->Init( frame, pNode->state, FLT_MAX, FLT_MAX, 0 );
641
+ }
642
+ }
643
+ }
644
+ }
645
+
646
+
647
+ #ifdef DEBUG
648
+ /*
649
+ void MicroPather::DumpStats()
650
+ {
651
+ int hashTableEntries = 0;
652
+ for( int i=0; i<HASH_SIZE; ++i )
653
+ if ( hashTable[i] )
654
+ ++hashTableEntries;
655
+
656
+ int pathNodeBlocks = 0;
657
+ for( PathNode* node = pathNodeMem; node; node = node[ALLOCATE-1].left )
658
+ ++pathNodeBlocks;
659
+ printf( "HashTableEntries=%d/%d PathNodeBlocks=%d [%dk] PathNodes=%d SolverCalled=%d\n",
660
+ hashTableEntries, HASH_SIZE, pathNodeBlocks,
661
+ pathNodeBlocks*ALLOCATE*sizeof(PathNode)/1024,
662
+ pathNodeCount,
663
+ frame );
664
+ }
665
+ */
666
+ #endif
667
+
668
+
669
+ void MicroPather::StatesInPool( MP_VECTOR< void* >* stateVec )
670
+ {
671
+ stateVec->clear();
672
+ pathNodePool.AllStates( frame, stateVec );
673
+ }
674
+
675
+
676
+ void PathNodePool::AllStates( unsigned frame, MP_VECTOR< void* >* stateVec )
677
+ {
678
+ for ( Block* b=blocks; b; b=b->nextBlock )
679
+ {
680
+ for( unsigned i=0; i<allocate; ++i )
681
+ {
682
+ if ( b->pathNode[i].frame == frame )
683
+ stateVec->push_back( b->pathNode[i].state );
684
+ }
685
+ }
686
+ }
687
+
688
+
689
+ PathCache::PathCache( int _allocated )
690
+ {
691
+ mem = new Item[_allocated];
692
+ memset( mem, 0, sizeof(*mem)*_allocated );
693
+ allocated = _allocated;
694
+ nItems = 0;
695
+ hit = 0;
696
+ miss = 0;
697
+ }
698
+
699
+
700
+ PathCache::~PathCache()
701
+ {
702
+ delete [] mem;
703
+ }
704
+
705
+
706
+ void PathCache::Reset()
707
+ {
708
+ if ( nItems ) {
709
+ memset( mem, 0, sizeof(*mem)*allocated );
710
+ nItems = 0;
711
+ hit = 0;
712
+ miss = 0;
713
+ }
714
+ }
715
+
716
+
717
+ void PathCache::Add( const MP_VECTOR< void* >& path, const MP_VECTOR< float >& cost )
718
+ {
719
+ if ( nItems + (int)path.size() > allocated*3/4 ) {
720
+ return;
721
+ }
722
+
723
+ for( unsigned i=0; i<path.size()-1; ++i ) {
724
+ // example: a->b->c->d
725
+ // Huge memory saving to only store 3 paths to 'd'
726
+ // Can put more in cache with also adding path to b, c, & d
727
+ // But uses much more memory. Experiment with this commented
728
+ // in and out and how to set.
729
+
730
+ void* end = path[path.size()-1];
731
+ Item item = { path[i], end, path[i+1], cost[i] };
732
+ AddItem( item );
733
+ }
734
+ }
735
+
736
+
737
+ void PathCache::AddNoSolution( void* end, void* states[], int count )
738
+ {
739
+ if ( count + nItems > allocated*3/4 ) {
740
+ return;
741
+ }
742
+
743
+ for( int i=0; i<count; ++i ) {
744
+ Item item = { states[i], end, 0, FLT_MAX };
745
+ AddItem( item );
746
+ }
747
+ }
748
+
749
+
750
+ int PathCache::Solve( void* start, void* end, MP_VECTOR< void* >* path, float* totalCost )
751
+ {
752
+ const Item* item = Find( start, end );
753
+ if ( item ) {
754
+ if ( item->cost == FLT_MAX ) {
755
+ ++hit;
756
+ return MicroPather::NO_SOLUTION;
757
+ }
758
+
759
+ path->clear();
760
+ path->push_back( start );
761
+ *totalCost = 0;
762
+
763
+ for ( ;start != end; start=item->next, item=Find(start, end) ) {
764
+ MPASSERT( item );
765
+ *totalCost += item->cost;
766
+ path->push_back( item->next );
767
+ }
768
+ ++hit;
769
+ return MicroPather::SOLVED;
770
+ }
771
+ ++miss;
772
+ return MicroPather::NOT_CACHED;
773
+ }
774
+
775
+
776
+ void PathCache::AddItem( const Item& item )
777
+ {
778
+ MPASSERT( allocated );
779
+ unsigned index = item.Hash() % allocated;
780
+ while( true ) {
781
+
782
+ if ( mem[index].Empty() ) {
783
+ mem[index] = item;
784
+ ++nItems;
785
+ #ifdef DEBUG_CACHING
786
+ GLOUTPUT(( "Add: start=%x next=%x end=%x\n", item.start, item.next, item.end ));
787
+ #endif
788
+ break;
789
+ }
790
+ else if ( mem[index].KeyEqual( item ) ) {
791
+ MPASSERT( (mem[index].next && item.next) || (mem[index].next==0 && item.next == 0) );
792
+ // do nothing; in cache
793
+ break;
794
+ }
795
+ ++index;
796
+ if ( index == allocated )
797
+ index = 0;
798
+ }
799
+ }
800
+
801
+
802
+ const PathCache::Item* PathCache::Find( void* start, void* end )
803
+ {
804
+ MPASSERT( allocated );
805
+ Item fake = { start, end, 0, 0 };
806
+ unsigned index = fake.Hash() % allocated;
807
+ while( true ) {
808
+ if ( mem[index].Empty() ) {
809
+ return 0;
810
+ }
811
+ if ( mem[index].KeyEqual( fake )) {
812
+ return mem + index;
813
+ }
814
+ ++index;
815
+ if ( index == allocated )
816
+ index = 0;
817
+ }
818
+ }
819
+
820
+
821
+ void MicroPather::GetCacheData( CacheData* data )
822
+ {
823
+ memset( data, 0, sizeof(*data) );
824
+
825
+ if ( pathCache ) {
826
+ data->nBytesAllocated = pathCache->AllocatedBytes();
827
+ data->nBytesUsed = pathCache->UsedBytes();
828
+ data->memoryFraction = (float)( (double)data->nBytesUsed / (double)data->nBytesAllocated );
829
+
830
+ data->hit = pathCache->hit;
831
+ data->miss = pathCache->miss;
832
+ if ( data->hit + data->miss ) {
833
+ data->hitFraction = (float)( (double)(data->hit) / (double)(data->hit + data->miss) );
834
+ }
835
+ else {
836
+ data->hitFraction = 0;
837
+ }
838
+ }
839
+ }
840
+
841
+
842
+
843
+ int MicroPather::Solve( void* startNode, void* endNode, MP_VECTOR< void* >* path, float* cost )
844
+ {
845
+ // Important to clear() in case the caller doesn't check the return code. There
846
+ // can easily be a left over path from a previous call.
847
+ path->clear();
848
+
849
+ #ifdef DEBUG_PATH
850
+ printf( "Path: " );
851
+ graph->PrintStateInfo( startNode );
852
+ printf( " --> " );
853
+ graph->PrintStateInfo( endNode );
854
+ printf( " min cost=%f\n", graph->LeastCostEstimate( startNode, endNode ) );
855
+ #endif
856
+
857
+ *cost = 0.0f;
858
+
859
+ if ( startNode == endNode )
860
+ return START_END_SAME;
861
+
862
+ if ( pathCache ) {
863
+ int cacheResult = pathCache->Solve( startNode, endNode, path, cost );
864
+ if ( cacheResult == SOLVED || cacheResult == NO_SOLUTION ) {
865
+ #ifdef DEBUG_CACHING
866
+ GLOUTPUT(( "PathCache hit. result=%s\n", cacheResult == SOLVED ? "solved" : "no_solution" ));
867
+ #endif
868
+ return cacheResult;
869
+ }
870
+ #ifdef DEBUG_CACHING
871
+ GLOUTPUT(( "PathCache miss\n" ));
872
+ #endif
873
+ }
874
+
875
+ ++frame;
876
+
877
+ OpenQueue open( graph );
878
+ ClosedSet closed( graph );
879
+
880
+ PathNode* newPathNode = pathNodePool.GetPathNode( frame,
881
+ startNode,
882
+ 0,
883
+ graph->LeastCostEstimate( startNode, endNode ),
884
+ 0 );
885
+
886
+ open.Push( newPathNode );
887
+ stateCostVec.resize(0);
888
+ nodeCostVec.resize(0);
889
+
890
+ while ( !open.Empty() )
891
+ {
892
+
893
+ PathNode* node = open.Pop();
894
+
895
+ if ( node->state == endNode )
896
+ {
897
+ GoalReached( node, startNode, endNode, path );
898
+ *cost = node->costFromStart;
899
+ #ifdef DEBUG_PATH
900
+ DumpStats();
901
+ #endif
902
+ return SOLVED;
903
+ }
904
+ else
905
+ {
906
+ closed.Add( node );
907
+ // We have not reached the goal - add the neighbors.
908
+ GetNodeNeighbors( node, &nodeCostVec );
909
+
910
+ for( int i=0; i<node->numAdjacent; ++i )
911
+ {
912
+ // Not actually a neighbor, but useful. Filter out infinite cost.
913
+ if ( nodeCostVec[i].cost == FLT_MAX ) {
914
+ continue;
915
+ }
916
+ PathNode* child = nodeCostVec[i].node;
917
+ float newCost = node->costFromStart + nodeCostVec[i].cost;
918
+ PathNode* inOpen = child->inOpen ? child : 0;
919
+ PathNode* inClosed = child->inClosed ? child : 0;
920
+ PathNode* inEither = (PathNode*)( ((MP_UPTR)inOpen) | ((MP_UPTR)inClosed) );
921
+
922
+ MPASSERT( inEither != node );
923
+ MPASSERT( !( inOpen && inClosed ) );
924
+ if ( inEither ) {
925
+ if ( newCost < child->costFromStart ) {
926
+ child->parent = node;
927
+ child->costFromStart = newCost;
928
+ child->estToGoal = graph->LeastCostEstimate( child->state, endNode );
929
+ child->CalcTotalCost();
930
+ if ( inOpen ) {
931
+ open.Update( child );
932
+ }
933
+ }
934
+ }
935
+ else {
936
+ child->parent = node;
937
+ child->costFromStart = newCost;
938
+ child->estToGoal = graph->LeastCostEstimate( child->state, endNode ),
939
+ child->CalcTotalCost();
940
+ MPASSERT( !child->inOpen && !child->inClosed );
941
+ open.Push( child );
942
+ }
943
+ }
944
+ }
945
+ }
946
+ #ifdef DEBUG_PATH
947
+ DumpStats();
948
+ #endif
949
+ if ( pathCache ) {
950
+ // Could add a bunch more with a little tracking.
951
+ pathCache->AddNoSolution( endNode, &startNode, 1 );
952
+ }
953
+ return NO_SOLUTION;
954
+ }
955
+
956
+
957
+ int MicroPather::SolveForNearStates( void* startState, MP_VECTOR< StateCost >* near, float maxCost )
958
+ {
959
+ /* http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
960
+
961
+ 1 function Dijkstra(Graph, source):
962
+ 2 for each vertex v in Graph: // Initializations
963
+ 3 dist[v] := infinity // Unknown distance function from source to v
964
+ 4 previous[v] := undefined // Previous node in optimal path from source
965
+ 5 dist[source] := 0 // Distance from source to source
966
+ 6 Q := the set of all nodes in Graph
967
+ // All nodes in the graph are unoptimized - thus are in Q
968
+ 7 while Q is not empty: // The main loop
969
+ 8 u := vertex in Q with smallest dist[]
970
+ 9 if dist[u] = infinity:
971
+ 10 break // all remaining vertices are inaccessible from source
972
+ 11 remove u from Q
973
+ 12 for each neighbor v of u: // where v has not yet been removed from Q.
974
+ 13 alt := dist[u] + dist_between(u, v)
975
+ 14 if alt < dist[v]: // Relax (u,v,a)
976
+ 15 dist[v] := alt
977
+ 16 previous[v] := u
978
+ 17 return dist[]
979
+ */
980
+
981
+ ++frame;
982
+
983
+ OpenQueue open( graph ); // nodes to look at
984
+ ClosedSet closed( graph );
985
+
986
+ nodeCostVec.resize(0);
987
+ stateCostVec.resize(0);
988
+
989
+ PathNode closedSentinel;
990
+ closedSentinel.Clear();
991
+ closedSentinel.Init( frame, 0, FLT_MAX, FLT_MAX, 0 );
992
+ closedSentinel.next = closedSentinel.prev = &closedSentinel;
993
+
994
+ PathNode* newPathNode = pathNodePool.GetPathNode( frame, startState, 0, 0, 0 );
995
+ open.Push( newPathNode );
996
+
997
+ while ( !open.Empty() )
998
+ {
999
+ PathNode* node = open.Pop(); // smallest dist
1000
+ closed.Add( node ); // add to the things we've looked at
1001
+ closedSentinel.AddBefore( node );
1002
+
1003
+ if ( node->totalCost > maxCost )
1004
+ continue; // Too far away to ever get here.
1005
+
1006
+ GetNodeNeighbors( node, &nodeCostVec );
1007
+
1008
+ for( int i=0; i<node->numAdjacent; ++i )
1009
+ {
1010
+ MPASSERT( node->costFromStart < FLT_MAX );
1011
+ float newCost = node->costFromStart + nodeCostVec[i].cost;
1012
+
1013
+ PathNode* inOpen = nodeCostVec[i].node->inOpen ? nodeCostVec[i].node : 0;
1014
+ PathNode* inClosed = nodeCostVec[i].node->inClosed ? nodeCostVec[i].node : 0;
1015
+ MPASSERT( !( inOpen && inClosed ) );
1016
+ PathNode* inEither = inOpen ? inOpen : inClosed;
1017
+ MPASSERT( inEither != node );
1018
+
1019
+ if ( inEither && inEither->costFromStart <= newCost ) {
1020
+ continue; // Do nothing. This path is not better than existing.
1021
+ }
1022
+ // Groovy. We have new information or improved information.
1023
+ PathNode* child = nodeCostVec[i].node;
1024
+ MPASSERT( child->state != newPathNode->state ); // should never re-process the parent.
1025
+
1026
+ child->parent = node;
1027
+ child->costFromStart = newCost;
1028
+ child->estToGoal = 0;
1029
+ child->totalCost = child->costFromStart;
1030
+
1031
+ if ( inOpen ) {
1032
+ open.Update( inOpen );
1033
+ }
1034
+ else if ( !inClosed ) {
1035
+ open.Push( child );
1036
+ }
1037
+ }
1038
+ }
1039
+ near->clear();
1040
+
1041
+ for( PathNode* pNode=closedSentinel.next; pNode != &closedSentinel; pNode=pNode->next ) {
1042
+ if ( pNode->totalCost <= maxCost ) {
1043
+ StateCost sc;
1044
+ sc.cost = pNode->totalCost;
1045
+ sc.state = pNode->state;
1046
+
1047
+ near->push_back( sc );
1048
+ }
1049
+ }
1050
+ #ifdef DEBUG
1051
+ for( unsigned i=0; i<near->size(); ++i ) {
1052
+ for( unsigned k=i+1; k<near->size(); ++k ) {
1053
+ MPASSERT( (*near)[i].state != (*near)[k].state );
1054
+ }
1055
+ }
1056
+ #endif
1057
+
1058
+ return SOLVED;
1059
+ }
1060
+
1061
+
1062
+