9 #include <unordered_set>
42 using std::make_unique;
47 using std::unique_ptr;
48 using std::unordered_set;
79 Context::PathFindingThread::PathFindingThread(
Context* context,
int idx):
80 Thread(
"wscontext-pathfindingthread"),
83 timeStateStarted(-1LL),
85 alternativeEndSteps(0),
87 pathFindingMutex(
"pathfindingthread-mutex"),
88 pathFindingCancelMutex(
"pathfindingthread-cancel-mutex"),
89 worldActionsMutex(
"pathfindingthread-world-actions-mutex")
93 pathFinding = make_unique<tdme::utilities::PathFinding>(
world.get(),
true, 1000, 1.8f, 0.4f, 0.81f, 0.4f,
context->
skipOnBodyCollisionTypeIdMask, 5, 0.5f, 2.0f);
99 void Context::PathFindingThread::PathFindingThread::reset() {
100 this->state = STATE_IDLE;
101 this->timeStateStarted = -1LL;
102 this->logicId.clear();
103 this->startPosition.set(0.0f, 0.0f, 0.0f);
104 this->endPosition.set(0.0f, 0.0f, 0.0f);
105 this->actorId.clear();
106 this->alternativeEndSteps = 0;
107 this->customTest =
nullptr;
109 this->flowMap =
nullptr;
110 this->createFlowMap =
false;
111 this->flowMapWidth = 0.0f;
112 this->flowMapDepth = 0.0f;
113 this->flowMap =
nullptr;
117 worldActionsMutex.lock();
118 worldActions.push_back(action);
119 worldActionsMutex.unlock();
123 const string& logicId,
124 const string& actorId,
127 vector<Vector3>& path,
128 int alternativeEndSteps,
131 const vector<Vector3> flowMapEndPositions,
132 const float flowMapWidth,
133 const float flowMapDepth,
136 if (pathFindingMutex.tryLock() ==
false) {
137 return State::STATE_TRYLOCK_FAILED;
142 case STATE_PATHFINDING:
143 if (this->actorId != actorId) {
144 pathFindingMutex.unlock();
145 return State::STATE_PATHFINDING_OTHER;
147 pathFindingMutex.unlock();
148 return STATE_PATHFINDING;
149 case STATE_PATHFINDING_FAILED:
150 if (this->actorId != actorId) {
151 auto now = Time::getCurrentMillis();
152 if (now - timeStateStarted > 1000LL) {
153 Console::println(
"Context::PathFindingThread[" + to_string(idx) +
"]::findPath(): path finding result timeout: " + logicId +
": resetting!");
156 pathFindingMutex.unlock();
157 return State::STATE_PATHFINDING_OTHER;
160 pathFindingMutex.unlock();
161 return STATE_PATHFINDING_FAILED;
162 case STATE_PATHFINDING_SUCCESS:
164 if (this->actorId != actorId) {
165 auto now = Time::getCurrentMillis();
166 if (now - timeStateStarted > 1000LL) {
167 Console::println(
"Context::PathFindingThread[" + to_string(idx) +
"]::findPath(): path finding result timeout: " + logicId +
": resetting!");
170 pathFindingMutex.unlock();
171 return State::STATE_PATHFINDING_OTHER;
174 if (this->endPosition.
equals(endPosition) ==
false) {
175 Console::println(
"Context::PathFindingThread[" + to_string(idx) +
"]::findPath(): " + actorId +
": position changed!");
177 auto returnState = endPosition.
equals(this->endPosition) ==
true?STATE_PATHFINDING_SUCCESS:State::STATE_PATHFINDING_OTHER;
178 if (createFlowMap ==
true) {
179 if (returnState ==
false) {
181 this->flowMap =
nullptr;
183 *flowMap = this->flowMap;
187 pathFindingMutex.unlock();
193 auto flowMapRequestIt = flowMapRequests.find(actorId);
194 if (flowMapRequestIt != flowMapRequests.end()) {
195 auto& flowMapRequest = flowMapRequestIt->second;
196 if (flowMapRequest.flowMap !=
nullptr) {
197 flowMapRequest.flowMap->releaseReference();
199 flowMapRequests.erase(flowMapRequestIt);
201 this->logicId = logicId;
202 this->startPosition = startPosition;
203 this->endPosition = endPosition;
204 this->actorId = actorId;
205 this->alternativeEndSteps = alternativeEndSteps;
206 this->customTest = customTest;
207 this->createFlowMap = createFlowMap;
208 this->flowMapWidth = flowMapWidth;
209 this->flowMapDepth = flowMapDepth;
210 this->flowMap =
nullptr;
211 state = STATE_PATHFINDING;
212 timeStateStarted = Time::getCurrentMillis();
213 pathFindingMutex.unlock();
214 return STATE_PATHFINDING;
218 if (pathFindingMutex.tryLock() ==
false)
return FLOWMAPEXTENSIONSTATE_TRYLOCK_FAILED;
219 auto flowMapRequestIt = flowMapRequests.find(actorId);
220 if (flowMapRequestIt == flowMapRequests.end()) {
221 pathFindingMutex.unlock();
222 return FLOWMAPEXTENSIONSTATE_REQUEST_NONE;
224 auto& flowMapRequest = flowMapRequestIt->second;
225 *flowMap = flowMapRequest.flowMap;
226 flowMapRequest.flowMap =
nullptr;
227 if (flowMapRequest.pathIdx >= flowMapRequest.path.size()) {
228 flowMapRequests.erase(flowMapRequestIt);
229 pathFindingMutex.unlock();
230 return FLOWMAPEXTENSIONSTATE_REQUEST_FINISHED;
232 pathFindingMutex.unlock();
233 return FLOWMAPEXTENSIONSTATE_REQUEST_NOTFINISHED;
237 pathFindingCancelMutex.lock();
238 cancelActorIds.push_back(actorId);
239 pathFindingCancelMutex.unlock();
243 Console::println(
string(context->server ==
true?
"SERVER::":
"CLIENT") +
"|ws::Context::PathFindingThread[" + to_string(idx) +
"]::run(): init");
244 while (isStopRequested() ==
false) {
246 context->logicsMutex->lock();
247 worldActionsMutex.lock();
248 auto worldActionsCopy = worldActions;
249 worldActions.clear();
250 worldActionsMutex.unlock();
251 if (worldActionsCopy.size() > 0) {
254 for (
const auto& worldActionStruct: worldActionsCopy) {
255 if (worldActionStruct.action == WorldActionStruct::ACTION_ADDED) {
256 if ((worldActionStruct.collisionTypeId & context->bodyCollisionTypeIdCloneMask) != 0) {
257 world->addStaticRigidBody(
258 worldActionStruct.id,
259 worldActionStruct.collisionTypeId,
260 worldActionStruct.enabled,
261 worldActionStruct.transform,
262 worldActionStruct.friction,
263 worldActionStruct.boundingVolumes
267 if (worldActionStruct.action == WorldActionStruct::ACTION_REMOVED) {
268 if (worldActionStruct.collisionTypeId & context->bodyCollisionTypeIdCloneMask != 0) {
275 worldActions.clear();
276 context->logicsMutex->unlock();
279 pathFindingMutex.lock();
280 pathFindingCancelMutex.lock();
281 for (
const auto& actorId: cancelActorIds) {
290 if (this->actorId != actorId)
continue;
291 auto flowMapRequestIt = flowMapRequests.find(actorId);
292 if (flowMapRequestIt != flowMapRequests.end()) {
293 auto& flowMapRequest = flowMapRequestIt->second;
294 if (flowMapRequest.flowMap !=
nullptr) {
295 flowMapRequest.flowMap->releaseReference();
297 flowMapRequests.erase(flowMapRequestIt);
300 context->getPathFinding()->notifyCancel(actorId);
303 cancelActorIds.clear();
304 pathFindingCancelMutex.unlock();
308 case STATE_PATHFINDING:
309 if (createFlowMap ==
true) {
313 context->bodyCollisionTypeIdMask,
318 timeStateStarted = Time::getCurrentMillis();
320 if (path.size() > 20) {
325 flowMapRequest.
path = path;
326 flowMapRequest.
flowMap =
nullptr;
327 flowMapRequests[actorId] = flowMapRequest;
329 vector<Vector3> partialPath(Math::min(path.size() - pathIdx, 20));
330 copy(path.begin() + pathIdx, path.begin() + pathIdx + Math::min(path.size() - pathIdx, 20), partialPath.begin());
332 { partialPath[partialPath.size() - 1] },
336 context->bodyCollisionTypeIdMask,
338 path.size() > 20?
false:true
340 state = STATE_PATHFINDING_SUCCESS;
342 timeStateStarted = Time::getCurrentMillis();
343 state = STATE_PATHFINDING_FAILED;
349 context->bodyCollisionTypeIdMask,
354 timeStateStarted = Time::getCurrentMillis();
355 state = STATE_PATHFINDING_SUCCESS;
357 timeStateStarted = Time::getCurrentMillis();
358 state = STATE_PATHFINDING_FAILED;
363 for (
auto& [flowMapRequestId, flowMapRequest]: flowMapRequests) {
364 if (flowMapRequest.flowMap !=
nullptr)
continue;
365 auto pathIdx = flowMapRequest.pathIdx - 2;
366 auto partialPathLength = Math::min(flowMapRequest.path.size() - pathIdx, 22);
367 vector<Vector3> partialPath(partialPathLength);
368 copy(flowMapRequest.path.begin() + pathIdx, flowMapRequest.path.begin() + pathIdx + partialPathLength, partialPath.begin());
369 flowMapRequest.flowMap =
pathFinding->createFlowMap(
370 { partialPath[partialPath.size() - 1] },
372 flowMapRequest.flowMapWidth,
373 flowMapRequest.flowMapDepth,
374 context->bodyCollisionTypeIdMask,
376 pathIdx >= path.size()?
true:false
378 flowMapRequest.pathIdx+= Math::min(flowMapRequest.path.size() - flowMapRequest.pathIdx, 20);
382 pathFindingMutex.unlock();
387 Console::println(
string(context->server ==
true?
"SERVER::":
"CLIENT") +
"|ws::Context::PathFindingThread[" + to_string(idx) +
"]::run(): done");
391 pathFindingMutex.lock();
392 auto _actorId = actorId;
393 pathFindingMutex.unlock();
399 actorThreadMapMutex(
"wspathfinding-threadmap-mutex")
405 this->threadCount = threadCount > 0?threadCount:Math::clamp(
static_cast<int>(Math::ceil(Thread::getHardwareThreadCount() / 3)), 1, 4);
409 threads.resize(threadCount);
410 for (
auto i = 0; i < threads.size(); i++) {
411 threads[i] = make_unique<PathFindingThread>(context, i);
413 for (
const auto& thread: threads) thread->start();
417 for (
const auto& thread: threads) thread->stop();
418 for (
const auto& thread: threads) thread->join();
423 for (
const auto& thread: threads) thread->addWorldAction(action);
427 for (
const auto& thread: threads) thread->reset();
431 const string& logicId,
432 const string& actorId,
435 vector<Vector3>& path,
436 int alternativeEndSteps,
439 const vector<Vector3> flowMapEndPositions,
440 const float flowMapWidth,
441 const float flowMapDepth,
444 actorThreadMapMutex.lock();
445 auto actorThreadMapIt = actorThreadMap.find(actorId);
446 actorThreadMapMutex.unlock();
447 auto threadIdx = actorThreadMapIt == actorThreadMap.end()?-1:actorThreadMapIt->second;
448 if (threadIdx != -1) {
449 auto threadPathFindingState = threads[threadIdx]->findPath(
463 switch(threadPathFindingState) {
465 actorThreadMapMutex.lock();
466 actorThreadMap.erase(actorId);
467 actorThreadMapMutex.unlock();
472 actorThreadMapMutex.lock();
473 actorThreadMap.erase(actorId);
474 actorThreadMapMutex.unlock();
479 actorThreadMapMutex.lock();
480 actorThreadMap.erase(actorId);
481 actorThreadMapMutex.unlock();
484 actorThreadMapMutex.lock();
485 actorThreadMap.erase(actorId);
486 actorThreadMapMutex.unlock();
490 for (
const auto& thread: threads) {
491 auto threadPathFindingState = thread->findPath(
505 switch(threadPathFindingState) {
513 actorThreadMapMutex.lock();
514 actorThreadMap[actorId] = thread->getThreadIdx();
515 actorThreadMapMutex.unlock();
518 actorThreadMapMutex.lock();
519 actorThreadMap.erase(actorId);
520 actorThreadMapMutex.unlock();
523 actorThreadMapMutex.lock();
524 actorThreadMap.erase(actorId);
525 actorThreadMapMutex.unlock();
534 for (
const auto& thread: threads) {
535 auto lastExtensionState = thread->getFlowMapExtension(actorId, flowMap);
536 switch(lastExtensionState) {
547 for (
const auto& thread: threads) thread->cancel(actorId);
551 actorThreadMapMutex.lock();
552 actorThreadMap.erase(actorId);
553 actorThreadMapMutex.unlock();
559 void Context::ContextWorldListener::onAddedBody(
const string&
id, Body::BodyType type, uint16_t collisionTypeId,
bool enabled,
const Transform& transform,
float restitution,
float friction,
float mass,
const Vector3& inertiaTensor,
const vector<BoundingVolume*>& boundingVolumes,
bool hierarchy) {
560 if (type != Body::BODYTYPE_STATIC)
return;
564 worldAction.
type = type;
570 worldAction.
mass = mass;
574 context->getPathFinding()->addWorldAction(worldAction);
578 if (type != Body::BODYTYPE_STATIC)
return;
582 worldAction.
type = type;
584 context->getPathFinding()->addWorldAction(worldAction);
596 Console::println(
"Context::Context(): " + to_string(
server));
601 Console::println(
"Context::~Context()");
621 Console::println(
"Context::shutdown()");
626 for (
auto logic:
logics)
delete logic;
632 Console::println(
string(
server ==
true?
"SERVER":
"CLIENT") +
"|Context::addLogic(): adding '" + logic->
getId() +
"'");
635 Console::println(
string(
server ==
true?
"SERVER":
"CLIENT") +
"|Context::addLogic(): NOT adding '" + logic->
getId() +
"', logic exists!");
640 if (logic->
getId() == newLogic->getId()) {
641 Console::println(
string(
server ==
true?
"SERVER":
"CLIENT") +
"|Context::addLogic(): NOT adding '" + logic->
getId() +
"', logic exists!");
653 auto now = Time::getCurrentMillis();
656 vector<string> packetsToRemove;
657 for (
const auto& [packetStateId, packetState]:
packetStates) {
658 if (packetState.timeCreated > now - 120000L) {
659 packetsToRemove.push_back(packetStateId);
662 for (
const auto& packetToRemove: packetsToRemove) {
671 _key+= logic->
getId();
683 auto& packetState = packetStateIt->second;
688 packetState.timeCreated = Time::getCurrentMillis();
700 _key+= logic->
getId();
Interface to audio module.
Object to be used with engine class.
Scene engine/physics connector.
void onAddedSubBody(const string &id, Body::BodyType type, uint16_t collisionTypeId, const string &subBodyParentId, const string &subBodyId, const Transform &transform, const vector< BoundingVolume * > &boundingVolumes) override
void onAddedBody(const string &id, Body::BodyType type, uint16_t collisionTypeId, bool enabled, const Transform &transform, float restitution, float friction, float mass, const Vector3 &inertiaTensor, const vector< BoundingVolume * > &boundingVolumes, bool hierarchy=false) override
ContextWorldListener(Context *context)
Constructor.
void onRemovedBody(const string &id, Body::BodyType type, uint16_t collisionTypeId) override
void onRemovedSubBody(const string &id, Body::BodyType type, uint16_t collisionTypeId, const string &subBodyParentId, const string &subBodyId) override
unique_ptr< tdme::utilities::PathFinding > pathFinding
State findPath(const string &logicId, const string &actorId, const Vector3 &startPosition, const Vector3 &endPosition, vector< Vector3 > &path, int alternativeEndSteps=0, PathFindingCustomTest *customTest=nullptr, bool createFlowMap=false, const vector< Vector3 > flowMapEndPositions=vector< Vector3 >(), const float flowMapWidth=0.0, const float flowMapDepth=0.0, FlowMap **flowMap=nullptr)
Find path.
@ STATE_PATHFINDING_SUCCESS
@ STATE_PATHFINDING_OTHER
@ STATE_PATHFINDING_FAILED
@ FLOWMAPEXTENSIONSTATE_REQUEST_FINISHED
@ FLOWMAPEXTENSIONSTATE_TRYLOCK_FAILED
@ FLOWMAPEXTENSIONSTATE_REQUEST_NOTFINISHED
@ FLOWMAPEXTENSIONSTATE_REQUEST_NONE
FlowMapExtensionState getFlowMapExtension(const string &actorId, FlowMap **flowMap)
Get flow map extension.
void cancel(const string &actorId)
Cancel.
void addWorldAction(const WorldActionStruct &action)
Add world action.
void reset()
Reset current path finding.
virtual ~PathFindingThread()
Public destructor.
unique_ptr< World > world
void notifyCancel(const string &actorId)
Notify a cancelled actor path finding.
void setThreadCount(int threadCount)
Set thread count.
PathFindingThread::State findPath(const string &logicId, const string &actorId, const Vector3 &startPosition, const Vector3 &endPosition, vector< Vector3 > &path, int alternativeEndSteps=0, PathFindingCustomTest *customTest=nullptr, bool createFlowMap=false, const vector< Vector3 > flowMapEndPositions=vector< Vector3 >(), const float flowMapWidth=0.0, const float flowMapDepth=0.0, FlowMap **flowMap=nullptr)
Find path.
void start()
Start path finding.
void addWorldAction(const Context::PathFindingThread::WorldActionStruct &action)
Add world action.
void shutdown()
Shutdown path finding.
void cancel(const string &actorId)
Cancel.
bool getFlowMapExtension(const string &actorId, FlowMap **flowMap)
Get flow map extension.
PathFinding(Context *context, int threadCount=0)
Public constructor.
void reset()
Reset current path finding.
unordered_map< string, PacketState > packetStates
unique_ptr< ContextWorldListener > worldListener
virtual void initialize()
Initialize logics.
bool doProcessPacket(NetworkLogic *logic, LogicNetworkPacket &packet, const string &key)
Returns if to process packet or not.
void unsetProcessPacket(NetworkLogic *logic, LogicNetworkPacket &packet, const string &key)
Unsets if to process packet or not.
volatile bool initialized
Context(bool server)
Public constructor.
virtual void doneUpdateLogics()
Logics finalizations, which is called once per logics updates.
vector< Logic * > newLogics
virtual void initUpdateLogics()
Logics initialization, which is called once per logics updates.
uint16_t bodyCollisionTypeIdCloneMask
virtual void shutdown()
Shut down logics.
unordered_map< string, Logic * > logicsById
virtual void doneUpdateEngine()
Update engine done, which is called once per frame after calling logic updateEngine() methods.
virtual ~Context()
Destructor.
unique_ptr< World > world
uint16_t skipOnBodyCollisionTypeIdMask
virtual void initUpdateEngine()
Update engine initialization, which is called once per frame before calling logic updateEngine() meth...
void addLogic(Logic *logic)
Add logic.
bool isReinjected() const
virtual void onLogicAdded()
On logic added.
virtual uint32_t getNetworkPacketTypeId()=0
Dynamic physics world class.
World * clone(const string &id, uint16_t collisionTypeIds=~0)
Clone this world.
GUI screen node that represents a screen that can be rendered via GUI system.
Vector3 class representing vector3 mathematical structure and operations with x, y,...
bool equals(const Vector3 &vector3, float tolerance=Math::EPSILON) const
Compares this vector3 with given vector3.
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
vector< BoundingVolume * > boundingVolumes
World listener which is about notifying adding or removing bodies.
Path finding custom test interface.