From 2daa880c0deefdb4d654b449a9b82b4cb86d3c9f Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Thu, 15 Oct 2015 13:14:59 +0200 Subject: [PATCH 01/11] Astar --- blakserv/astar.c | 577 ++++++++++++++++++ blakserv/astar.h | 90 +++ blakserv/blakserv.h | 3 + blakserv/blakserv.vcxproj | 2 + blakserv/blakserv.vcxproj.filters | 6 + blakserv/ccode.c | 2 +- blakserv/makefile | 3 +- blakserv/memory.c | 2 +- blakserv/memory.h | 2 +- blakserv/roofile.c | 469 +++++++------- blakserv/roofile.h | 9 +- kod/include/blakston.khd | 1 + .../holder/nomoveon/battler/monster.kod | 7 + 13 files changed, 948 insertions(+), 225 deletions(-) create mode 100644 blakserv/astar.c create mode 100644 blakserv/astar.h diff --git a/blakserv/astar.c b/blakserv/astar.c new file mode 100644 index 0000000000..49833f587b --- /dev/null +++ b/blakserv/astar.c @@ -0,0 +1,577 @@ +// Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +// All rights reserved. +// +// This software is distributed under a license that is described in +// the LICENSE file that accompanies it. +// +// Meridian is a registered trademark. +/* +* astar.c +* + +Improvements over the old implementation: + * Allocation of gridmemory is only done once when room is loaded + * Node information is split up between persistent data and data + which needs to be cleared for each algorithm and can be by single ZeroMemory + * Whether a node is in the closed set is not saved and looked up by an additional list, + but rather simply saved by a flag on that node. + * Uses a binary heap to store the open set +*/ + +#include "blakserv.h" + +#pragma region HEAP +// refers to the i-th element on the heap of a room +#define HEAP(r, i) ((r)->Astar.NodesData[i].heapslot) + +__inline bool AStarHeapCheck(room_type* Room, int i) +{ + int squares = Room->colshighres * Room->rowshighres; + + if (i >= squares) + return true; + + if (!HEAP(Room, i)) + return true; + + float our = HEAP(Room, i)->Data->combined; + int lchild = LCHILD(i); + int rchild = RCHILD(i); + + if (lchild < squares) + { + astar_node* node = HEAP(Room, lchild); + if (node) + { + if (node->Data->combined < our) + return false; + } + } + if (rchild < squares) + { + astar_node* node = HEAP(Room, rchild); + if (node) + { + if (node->Data->combined < our) + return false; + } + } + + bool retval = AStarHeapCheck(Room, lchild); + if (!retval) + return false; + + return AStarHeapCheck(Room, rchild); +} + +__inline void AStarWriteHeapToFile(room_type* Room) +{ + int rows = Room->rowshighres; + int cols = Room->colshighres; + char* rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 60000); + FILE *fp = fopen("heapdebug.txt", "a"); + if (fp) + { + int maxlayer = 9; + int treesize = Room->colshighres * Room->rowshighres; + int rangestart = 0; + int rangesize = 1; + for (int i = 1; i < maxlayer; i++) + { + if (treesize < rangestart + rangesize) + break; + + sprintf(rowstring, "L:%.2i", i); + + for (int k = 0; k < rangesize; k++) + { + astar_node* node = Room->Astar.NodesData[rangestart + k].heapslot; + + if (node) + sprintf(rowstring, "%s|%6.2f|", rowstring, node->Data->combined); + else + sprintf(rowstring, "%s|XXXXXX|", rowstring); + } + + sprintf(rowstring, "%s\n", rowstring); + fputs(rowstring, fp); + + rangestart = rangestart + rangesize; + rangesize *= 2; + } + sprintf(rowstring, "%s\n", rowstring); + fclose(fp); + } + FreeMemory(MALLOC_ID_ROOM, rowstring, 60000); +} + +__inline void AStarHeapSwap(room_type* Room, int idx1, int idx2) +{ + astar_node* node1 = HEAP(Room, idx1); + astar_node* node2 = HEAP(Room, idx2); + HEAP(Room, idx1) = node2; + HEAP(Room, idx2) = node1; + node1->Data->heapindex = idx2; + node2->Data->heapindex = idx1; +} + +__inline void AStarHeapMoveUp(room_type* Room, int Index) +{ + int i = Index; + while (i > 0 && HEAP(Room, i)->Data->combined < HEAP(Room, PARENT(i))->Data->combined) + { + AStarHeapSwap(Room, i, PARENT(i)); + i = PARENT(i); + } +} + +__inline void AStarHeapHeapify(room_type* Room, int Index) +{ + int i = Index; + do + { + int min = i; + if (HEAP(Room, LCHILD(i)) && HEAP(Room, LCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined) + min = LCHILD(i); + if (HEAP(Room, RCHILD(i)) && HEAP(Room, RCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined) + min = RCHILD(i); + if (min == i) + break; + AStarHeapSwap(Room, i, min); + i = min; + } + while (true); +} + +__inline void AStarHeapInsert(room_type* Room, astar_node* Node) +{ + // save index of this node + Node->Data->heapindex = Room->Astar.HeapSize; + + // add node at the end + HEAP(Room, Node->Data->heapindex) = Node; + + // increment + Room->Astar.HeapSize++; + + // push node up until in place + AStarHeapMoveUp(Room, Node->Data->heapindex); +} + +__inline void AStarHeapRemoveFirst(room_type* Room) +{ + int lastIdx = Room->Astar.HeapSize - 1; + + // no elements + if (lastIdx < 0) + return; + + // decrement size + Room->Astar.HeapSize--; + + // more than 1 + if (lastIdx > 0) + { + // put last at root + AStarHeapSwap(Room, 0, lastIdx); + + // zero out the previous root at swapped slot + HEAP(Room, lastIdx)->Data->heapindex = 0; + HEAP(Room, lastIdx) = NULL; + + // reorder tree + AStarHeapHeapify(Room, 0); + } + + // only one, clear head + else + { + HEAP(Room, 0)->Data->heapindex = 0; + HEAP(Room, 0) = NULL; + } +} +#pragma endregion + +#pragma region ASTAR +__inline void AStarAddBlockers(room_type *Room) +{ + Blocker* b; + + b = Room->Blocker; + while (b) + { + // Don't add self. + if (b->ObjectID == Room->Astar.ObjectID) + { + b = b->Next; + continue; + } + + // Get blocker coords + int row = (int)roundf(b->Position.Y / 256.0f); + int col = (int)roundf(b->Position.X / 256.0f); + + // Don't add blockers at the target coords. + if (abs(row - Room->Astar.EndNode->Row) < DESTBLOCKIGNORE && + abs(col - Room->Astar.EndNode->Col) < DESTBLOCKIGNORE) + { + b = b->Next; + continue; + } + + // Mark these nodes in A* grid blocked (our coord and +2 highres each dir) + for (int rowoffset = -2; rowoffset < 3; rowoffset++) + { + for (int coloffset = -2; coloffset < 3; coloffset++) + { + int r = row + rowoffset; + int c = col + coloffset; + + // outside + if (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) + continue; + + astar_node* node = &Room->Astar.Grid[r][c]; + node->Data->isBlocked = true; + } + } + b = b->Next; + } +} + +__inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) +{ + bool knows = (((*Node->Edges) & KnowsVal) == KnowsVal); + bool can; + Wall* blockWall; + + // not yet cached + if (!knows) + { + // bsp query + can = BSPCanMoveInRoom(Room, &Node->Location, &Neighbour->Location, Room->Astar.ObjectID, false, &blockWall, true); + + // save to cache + *Node->Edges |= (can) ? CanVal : KnowsVal; + } + + // retrieve answer from cache + else + can = (((*Node->Edges) & CanVal) == CanVal); + + return can; +} + +__inline void AStarProcessNeighbour(room_type* Room, astar_node* EndNode, astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) +{ + // skip any neighbour outside of grid or sector, already processed or blocked + if (!Neighbour || !Neighbour->Leaf || Neighbour->Data->isInClosedList || Neighbour->Data->isBlocked) + return; + + // can't walk edge from node to neighbour + if (!AStarCanWalkEdge(Room, Node, Neighbour, KnowsVal, CanVal)) + return; + + // heuristic (~ estimated distance from node to end) + // need to do this only once + if (Neighbour->Data->heuristic == 0.0f) + { + float dx = (float)abs(Neighbour->Col - EndNode->Col); + float dy = (float)abs(Neighbour->Row - EndNode->Row); + + // octile-distance + Neighbour->Data->heuristic = + COST * (dx + dy) + (COST_DIAG - 2.0f * COST) * fminf(dx, dy); + + // tie breaker and fixes h(nondiagonal) not lower exact cost + Neighbour->Data->heuristic *= 0.999f; + } + + // CASE 1) + // if this candidate has no parent yet (never visited before) + if (!Neighbour->Data->parent) + { + // cost to candidate is cost to Node + one step + Neighbour->Data->parent = Node; + Neighbour->Data->cost = Node->Data->cost + StepCost; + Neighbour->Data->combined = Neighbour->Data->cost + Neighbour->Data->heuristic; + + // add it sorted to the open list + AStarHeapInsert(Room, Neighbour); + } + + // CASE 2) + // we already got a path to this candidate + else + { + // our cost to the candidate + float newcost = Node->Data->cost + StepCost; + + // we're cheaper, so update the candidate + // the real cost matters here, not including the heuristic + if (newcost < Neighbour->Data->cost) + { + Neighbour->Data->parent = Node; + Neighbour->Data->cost = newcost; + Neighbour->Data->combined = newcost + Neighbour->Data->heuristic; + + // reorder it upwards in the heap tree, don't care about downordering + // since costs are lower and heuristic is always the same, + // it's guaranteed to be moved up + AStarHeapMoveUp(Room, Neighbour->Data->heapindex); + } + } +} + +__inline bool AStarProcessFirst(room_type* Room) +{ + // shortcuts + astar_node* node = HEAP(Room, 0); + astar_node* endNode = Room->Astar.EndNode; + + // openlist empty, unreachable + if (!node) + return false; + + // close enough, we're done, path found + if (abs(node->Col - endNode->Col) < CLOSEENOUGHDIST && + abs(node->Row - endNode->Row) < CLOSEENOUGHDIST) + { + Room->Astar.LastNode = node; + return false; + } + + /****************************************************************/ + + // straight neighbours + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[0], COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[2], COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[4], COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[6], COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); + + // diagonal neighbours + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[1], COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[3], COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[5], COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); + AStarProcessNeighbour(Room, endNode, node, node->Neighbours[7], COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); + + /****************************************************************/ + + // mark this node processed + node->Data->isInClosedList = true; + + // remove it from the open list + AStarHeapRemoveFirst(Room); + + return true; +} + +void AStarWriteGridToFile(room_type* Room) +{ + int rows = Room->rowshighres; + int cols = Room->colshighres; + + char *rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 50000); + + FILE *fp = fopen("griddebug.txt", "w"); + if (fp) + { + for (int row = 0; row < rows; row++) + { + sprintf(rowstring, "Row %3i- ", row); + for (int col = 0; col < cols; col++) + { + sprintf(rowstring, "%s|%7.3f|", rowstring, Room->Astar.Grid[row][col].Data->combined); + } + sprintf(rowstring, "%s \n", rowstring); + fputs(rowstring, fp); + } + fclose(fp); + } + + FreeMemory(MALLOC_ID_ROOM, rowstring, 50000); +} + +void AStarGenerateGrid(room_type* Room) +{ + // note: we allocate all memory for the astar_node_data of all squares together + // this is necessary so we can erase it with a single cheap ZeroMemory call.. + Room->Astar.NodesDataSize = Room->colshighres * Room->rowshighres * sizeof(astar_node_data); + + // allocate memory for all nodesdata (cleaned for each calculation) + Room->Astar.NodesData = (astar_node_data*)AllocateMemory( + MALLOC_ID_ASTAR, Room->Astar.NodesDataSize); + + // allocate memory to cache edges (BSP queries) + Room->Astar.EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); + + Room->Astar.EdgesCache = (unsigned short*)AllocateMemory( + MALLOC_ID_ASTAR, Room->Astar.EdgesCacheSize); + + AStarClearEdgesCache(Room); + + // allocate memory for the persistent nodesinfo (typical 2d array by **) + Room->Astar.Grid = (astar_node**)AllocateMemory( + MALLOC_ID_ASTAR, Room->rowshighres * sizeof(astar_node*)); + + // setup rows + for (int i = 0; i < Room->rowshighres; i++) + Room->Astar.Grid[i] = (astar_node*)AllocateMemory( + MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node)); + + // setup all squares + for (int i = 0; i < Room->rowshighres; i++) + { + for (int j = 0; j < Room->colshighres; j++) + { + astar_node* node = &Room->Astar.Grid[i][j]; + float f1, f2, f3; + node->Row = i; + node->Col = j; + + // floatingpoint coordinates of the center of the square in ROO fineness (for queries) + node->Location.X = j * 256.0f;// +128.0f; + node->Location.Y = i * 256.0f;// +128.0f; + + // mark if this square is inside or outside of room + BSPGetHeight(Room, &node->Location, &f1, &f2, &f3, &node->Leaf); + + int idx = i*Room->colshighres + j; + + // setup reference to data (costs etc.) in erasable mem area + node->Data = &Room->Astar.NodesData[idx]; + + // setup reference to edgesdata in edgescache + node->Edges = &Room->Astar.EdgesCache[idx]; + + // setup neighbour pointers + int r, c; + + // N + r = node->Row - 1; + c = node->Col + 0; + node->Neighbours[0] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // NE + r = node->Row - 1; + c = node->Col + 1; + node->Neighbours[1] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // E + r = node->Row + 0; + c = node->Col + 1; + node->Neighbours[2] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // SE + r = node->Row + 1; + c = node->Col + 1; + node->Neighbours[3] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // S + r = node->Row + 1; + c = node->Col + 0; + node->Neighbours[4] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // SW + r = node->Row + 1; + c = node->Col - 1; + node->Neighbours[5] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // W + r = node->Row + 0; + c = node->Col - 1; + node->Neighbours[6] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + // NW + r = node->Row - 1; + c = node->Col - 1; + node->Neighbours[7] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + } + } +} + +void AStarFreeGrid(room_type* Room) +{ + // free workdata mem + FreeMemory(MALLOC_ID_ASTAR, Room->Astar.NodesData, Room->Astar.NodesDataSize); + + // free edgescache mem + FreeMemory(MALLOC_ID_ASTAR, Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); + + // free each row mem + for (int i = 0; i < Room->rowshighres; i++) + FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid[i], Room->colshighres * sizeof(astar_node)); + + // free rowsmem + FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid, Room->rowshighres * sizeof(astar_node*)); +} + +void AStarClearEdgesCache(room_type* Room) +{ + ZeroMemory(Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); +} + +bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +{ + // convert coordinates from ROO floatingpoint to + // highres scale in integers + int startrow = (int)roundf(S->Y / 256.0f); + int startcol = (int)roundf(S->X / 256.0f); + int endrow = (int)roundf(E->Y / 256.0f); + int endcol = (int)roundf(E->X / 256.0f); + + // all 4 values must be within grid array bounds! + if (startrow < 0 || startrow >= Room->rowshighres || + startcol < 0 || startcol >= Room->colshighres || + endrow < 0 || endrow >= Room->rowshighres || + endcol < 0 || endcol >= Room->colshighres) + return false; + + /**********************************************************************/ + + // get start and endnode + astar_node* startnode = &Room->Astar.Grid[startrow][startcol]; + Room->Astar.EndNode = &Room->Astar.Grid[endrow][endcol]; + + // init the astar struct + Room->Astar.LastNode = NULL; + Room->Astar.ObjectID = ObjectID; + Room->Astar.HeapSize = 0; + + // prepare non-persistent astar grid data memory + ZeroMemory(Room->Astar.NodesData, Room->Astar.NodesDataSize); + + /**********************************************************************/ + + // mark nodes blocked by objects + AStarAddBlockers(Room); + + // insert startnode into heap-tree + AStarHeapInsert(Room, startnode); + + // the algorithm finishes if we either hit a node close enough to endnode + // or if there is no more entries in the open list (unreachable) + while (AStarProcessFirst(Room)) {} + + //AStarWriteGridToFile(Room); + //AStarWriteHeapToFile(Room); + /**********************************************************************/ + + // now let's walk back our parent pointers starting from the LastNode + // this chain represents the path (if it was unreachable, it's null) + astar_node* parent = Room->Astar.LastNode; + while (parent && parent->Data->parent != startnode) + parent = parent->Data->parent; + + // unreachable + if (!parent) + return false; + + // for diagonal moves mark to be long step (required for timer elapse) + if (abs(parent->Col - startnode->Col) && + abs(parent->Row - startnode->Row)) + { + *Flags |= ESTATE_LONG_STEP; + } + else + *Flags &= ~ESTATE_LONG_STEP; + + // set step endpoint + *P = parent->Location; + + return true; +} +#pragma endregion diff --git a/blakserv/astar.h b/blakserv/astar.h new file mode 100644 index 0000000000..2f5f940fe8 --- /dev/null +++ b/blakserv/astar.h @@ -0,0 +1,90 @@ +// Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +// All rights reserved. +// +// This software is distributed under a license that is described in +// the LICENSE file that accompanies it. +// +// Meridian is a registered trademark. +/* +* astar.h: +*/ + +#ifndef _ASTAR_H +#define _ASTAR_H + +#define CLOSEENOUGHDIST 3 +#define DESTBLOCKIGNORE 3 +#define ASTARENABLED 1 +#define NUMNEIGHBOURS 8 + +#define LCHILD(x) (2 * x + 1) +#define RCHILD(x) (2 * x + 2) +#define PARENT(x) ((x-1) / 2) + +#define COST 1.0f +#define COST_DIAG ((float)M_SQRT2) + +#define EDGECACHE_KNOWS_N 0x0001 +#define EDGECACHE_KNOWS_NE 0x0002 +#define EDGECACHE_KNOWS_E 0x0004 +#define EDGECACHE_KNOWS_SE 0x0008 +#define EDGECACHE_KNOWS_S 0x0010 +#define EDGECACHE_KNOWS_SW 0x0020 +#define EDGECACHE_KNOWS_W 0x0040 +#define EDGECACHE_KNOWS_NW 0x0080 +#define EDGECACHE_CAN_N 0x0101 +#define EDGECACHE_CAN_NE 0x0202 +#define EDGECACHE_CAN_E 0x0404 +#define EDGECACHE_CAN_SE 0x0808 +#define EDGECACHE_CAN_S 0x1010 +#define EDGECACHE_CAN_SW 0x2020 +#define EDGECACHE_CAN_W 0x4040 +#define EDGECACHE_CAN_NW 0x8080 + +typedef struct room_type room_type; +typedef struct BspLeaf BspLeaf; +typedef struct astar_node_data astar_node_data; +typedef struct astar_node astar_node; + +typedef struct astar_node_data +{ + float cost; + float heuristic; + float combined; + astar_node* parent; + astar_node* heapslot; + int heapindex; + bool isInClosedList; + bool isBlocked; +} astar_node_data; + +typedef struct astar_node +{ + int Row; + int Col; + V2 Location; + BspLeaf* Leaf; + astar_node_data* Data; + astar_node* Neighbours[NUMNEIGHBOURS]; + unsigned short* Edges; +} astar_node; + +typedef struct astar +{ + astar_node_data* NodesData; + int NodesDataSize; + unsigned short* EdgesCache; + int EdgesCacheSize; + astar_node** Grid; + astar_node* EndNode; + astar_node* LastNode; + int ObjectID; + int HeapSize; +} astar; + +void AStarGenerateGrid(room_type* Room); +void AStarFreeGrid(room_type* Room); +void AStarClearEdgesCache(room_type* Room); +bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); + +#endif /*#ifndef _ASTAR_H */ \ No newline at end of file diff --git a/blakserv/blakserv.h b/blakserv/blakserv.h index 912234cc9f..651c9760ce 100644 --- a/blakserv/blakserv.h +++ b/blakserv/blakserv.h @@ -241,6 +241,8 @@ char * GetLastErrorStr(); #include "stringinthash.h" #include "intstringhash.h" +#include "geometry.h" + #include "blakres.h" #include "channel.h" #include "kodbase.h" @@ -257,6 +259,7 @@ char * GetLastErrorStr(); #include "system.h" #include "loadrsc.h" #include "loadgame.h" +#include "astar.h" #include "roofile.h" #include "roomdata.h" #include "files.h" diff --git a/blakserv/blakserv.vcxproj b/blakserv/blakserv.vcxproj index b17bf0ee56..d41514efc8 100644 --- a/blakserv/blakserv.vcxproj +++ b/blakserv/blakserv.vcxproj @@ -168,6 +168,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server + @@ -242,6 +243,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server + diff --git a/blakserv/blakserv.vcxproj.filters b/blakserv/blakserv.vcxproj.filters index 4da1eda561..7e16fa6f6d 100644 --- a/blakserv/blakserv.vcxproj.filters +++ b/blakserv/blakserv.vcxproj.filters @@ -230,6 +230,9 @@ Header Files + + Header Files + @@ -436,6 +439,9 @@ Source Files + + Source Files + diff --git a/blakserv/ccode.c b/blakserv/ccode.c index 6fa8ee4a6b..31e8855bd1 100644 --- a/blakserv/ccode.c +++ b/blakserv/ccode.c @@ -2675,7 +2675,7 @@ int C_CanMoveInRoomBSP(int object_id, local_var_type *local_vars, e.Y = GRIDCOORDTOROO(row_dest.v.data, finerow_dest.v.data); Wall* blockWall; - ret_val.v.data = BSPCanMoveInRoom(&r->data, &s, &e, objectid.v.data, (move_outside_bsp.v.data != 0), &blockWall); + ret_val.v.data = BSPCanMoveInRoom(&r->data, &s, &e, objectid.v.data, (move_outside_bsp.v.data != 0), &blockWall, false); #if DEBUGMOVE //dprintf("MOVE:%i R:%i S:(%1.2f/%1.2f) E:(%1.2f/%1.2f)", ret_val.v.data, r->data.roomdata_id, s.X, s.Y, e.X, e.Y); diff --git a/blakserv/makefile b/blakserv/makefile index 005cd8f153..cc9436a9f3 100644 --- a/blakserv/makefile +++ b/blakserv/makefile @@ -91,7 +91,8 @@ OBJS = \ $(OUTDIR)\files.obj \ $(OUTDIR)\sprocket.obj \ $(OUTDIR)\database.obj \ - + $(OUTDIR)\astar.obj \ + all : makedirs $(OUTDIR)\blakserv.exe $(OUTDIR)\rscload.obj : $(TOPDIR)\util\rscload.c diff --git a/blakserv/memory.c b/blakserv/memory.c index dc7439ea9b..3b1dfdd80c 100644 --- a/blakserv/memory.c +++ b/blakserv/memory.c @@ -247,7 +247,7 @@ const char *memory_stat_names[] = "Systimer", "Nameid", "Class", "Message", "Object", "List", "Object properties", - "Configuration", "Rooms", + "Configuration", "Rooms", "Astar", "Admin constants", "Buffers", "Game loading", "Tables", "Socket blocks", "Game saving", diff --git a/blakserv/memory.h b/blakserv/memory.h index b2aedcfdf6..d9b63e0a43 100644 --- a/blakserv/memory.h +++ b/blakserv/memory.h @@ -21,7 +21,7 @@ enum MALLOC_ID_SYSTIMER, MALLOC_ID_NAMEID, MALLOC_ID_CLASS, MALLOC_ID_MESSAGE, MALLOC_ID_OBJECT, MALLOC_ID_LIST, MALLOC_ID_OBJECT_PROPERTIES, - MALLOC_ID_CONFIG, MALLOC_ID_ROOM, + MALLOC_ID_CONFIG, MALLOC_ID_ROOM, MALLOC_ID_ASTAR, MALLOC_ID_ADMIN_CONSTANTS, MALLOC_ID_BUFFER, MALLOC_ID_LOAD_GAME, MALLOC_ID_TABLE, MALLOC_ID_BLOCK, MALLOC_ID_SAVE_GAME, diff --git a/blakserv/roofile.c b/blakserv/roofile.c index a53796879e..64c19d26f7 100644 --- a/blakserv/roofile.c +++ b/blakserv/roofile.c @@ -634,7 +634,7 @@ bool BSPLineOfSight(room_type* Room, V3* S, V3* E) /*********************************************************************************************/ /* BSPCanMoveInRoom: Checks if you can walk a straight line from (S)tart to (E)nd */ /*********************************************************************************************/ -bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall) +bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall, bool SkipBlockers) { if (!Room || Room->TreeNodesCount == 0 || !S || !E) return false; @@ -655,6 +655,10 @@ bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOuts if (!roomok) return false; + // we're done if no need to check object blockers + if (SkipBlockers) + return true; + // otherwise also check against blockers Blocker* blocker = Room->Blocker; while (blocker) @@ -754,6 +758,11 @@ void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short New sector->CeilingTexture = (isReset ? sector->CeilingTextureOrig : NewTexture); } } + + // must invalidate cached astar edges +#if ASTARENABLED + AStarClearEdgesCache(Room); +#endif } /*********************************************************************************************/ @@ -784,6 +793,11 @@ void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Hei BSPUpdateLeafHeights(Room, sector, false); } } + + // must invalidate cached astar edges +#if ASTARENABLED + AStarClearEdgesCache(Room); +#endif } /*********************************************************************************************/ @@ -940,7 +954,7 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags // but must not give these back in piState *Flags &= ~MSTATE_MOVE_OUTSIDE_BSP; - + V2 se, stepend; V2SUB(&se, E, S); @@ -965,252 +979,265 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags V2SCALE(&se, scale); /****************************************************/ - // 1) try direct step towards destination first + // 1) test direct line towards destination first /****************************************************/ Wall* blockWall = NULL; - // note: we must verify the location the object is actually going to end up in KOD, - // this means we must round to the next closer kod-fineness value, - // so these values are also exactly expressable in kod coordinates. - // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units - V2ADD(&stepend, S, &se); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) + if (BSPCanMoveInRoom(Room, S, E, ObjectID, moveOutsideBSP, &blockWall, false)) { - *P = stepend; - *Flags &= ~ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; + // note: we must verify the location the object is actually going to end up in KOD, + // this means we must round to the next closer kod-fineness value, + // so these values are also exactly expressable in kod coordinates. + // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units + V2ADD(&stepend, S, &se); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags &= ~ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } } - + /****************************************************/ // 2) can't do direct step /****************************************************/ - bool isAvoiding = ((*Flags & ESTATE_AVOIDING) == ESTATE_AVOIDING); - bool isLeft = ((*Flags & ESTATE_CLOCKWISE) == ESTATE_CLOCKWISE); - - // not yet in clockwise or cclockwise mode - if (!isAvoiding) + // try get next step from astar path if enabled + if (ASTARENABLED && AStarGetStepTowards(Room, S, E, P, Flags, ObjectID)) + { + *Flags &= ~ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + else { - // if not blocked by a wall, roll a dice to decide - // how to get around the blocking obj. - if (!blockWall) - isLeft = (rand() % 2 == 1); + bool isAvoiding = ((*Flags & ESTATE_AVOIDING) == ESTATE_AVOIDING); + bool isLeft = ((*Flags & ESTATE_CLOCKWISE) == ESTATE_CLOCKWISE); - // blocked by wall, go first into 'slide-along' direction - // based on vector towards target - else + // not yet in clockwise or cclockwise mode + if (!isAvoiding) { - V2 p1p2; - V2SUB(&p1p2, &blockWall->P2, &blockWall->P1); - - // note: walls can be aligned in any direction like left->right, right->left, - // same with up->down and same also with the movement vector. - // The typical angle between vectors, acosf(..) is therefore insufficient to differ. - // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis) - // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped - // to the left or right decision. - float f1 = atan2f(se.Y, se.X); - float f2 = atan2f(p1p2.Y, p1p2.X); - float df = f1 - f2; - - bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2); - bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI); - bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2)); - bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f); - bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2); - bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI); - bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2)); - bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f); - - isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true; + // if not blocked by a wall, roll a dice to decide + // how to get around the blocking obj. + if (!blockWall) + isLeft = (rand() % 2 == 1); - /*if (isLeft) - dprintf("trying left first r: %f", df); + // blocked by wall, go first into 'slide-along' direction + // based on vector towards target else - dprintf("trying right first r: %f", df);*/ + { + V2 p1p2; + V2SUB(&p1p2, &blockWall->P2, &blockWall->P1); + + // note: walls can be aligned in any direction like left->right, right->left, + // same with up->down and same also with the movement vector. + // The typical angle between vectors, acosf(..) is therefore insufficient to differ. + // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis) + // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped + // to the left or right decision. + float f1 = atan2f(se.Y, se.X); + float f2 = atan2f(p1p2.Y, p1p2.X); + float df = f1 - f2; + + bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2); + bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI); + bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2)); + bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f); + bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2); + bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI); + bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2)); + bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f); + + isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true; + + /*if (isLeft) + dprintf("trying left first r: %f", df); + else + dprintf("trying right first r: %f", df);*/ + } } - } - // must run this possibly twice - // e.g. left after right failed or right after left failed - for (int i = 0; i < 2; i++) - { - if (isLeft) + // must run this possibly twice + // e.g. left after right failed or right after left failed + for (int i = 0; i < 2; i++) { - V2 v = se; - - // try 22.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } - - // try 45° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) + if (isLeft) { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + V2 v = se; + + // try 22.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // try 67.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } - - // try 90° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 45° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // try 112.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 67.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // try 135° left - V2ROTATE(&v, (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 90° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // failed to circumvent by going left, switch to right - isLeft = false; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - } - else - { - V2 v = se; - - // try 22.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + // try 112.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // try 45° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + // try 135° left + V2ROTATE(&v, (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags |= ESTATE_CLOCKWISE; + return true; + } - // try 67.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; + // failed to circumvent by going left, switch to right + isLeft = false; *Flags |= ESTATE_AVOIDING; *Flags &= ~ESTATE_CLOCKWISE; - return true; - } - - // try 90° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; } - - // try 112.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) + else { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + V2 v = se; + + // try 22.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } - // try 135° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall)) - { - *P = stepend; + // try 45° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + + // try 67.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + + // try 90° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + + // try 112.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + + // try 135° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + *P = stepend; + *Flags |= ESTATE_AVOIDING; + *Flags &= ~ESTATE_CLOCKWISE; + return true; + } + + // failed to circumvent by going right, switch to left + isLeft = true; *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; + *Flags |= ESTATE_CLOCKWISE; } - - // failed to circumvent by going right, switch to left - isLeft = true; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; } } @@ -1880,6 +1907,12 @@ bool BSPLoadRoom(char *fname, room_type *room) } } + /*************************************************************************/ + /* GENERATE ASTAR PERSISTENT DATA */ + /*************************************************************************/ + + AStarGenerateGrid(room); + /****************************************************************************/ /****************************************************************************/ @@ -1933,6 +1966,8 @@ void BSPFreeRoom(room_type *room) BSPBlockerClear(room); + AStarFreeGrid(room); + /****************************************************************************/ /* SERVER PARTS */ /****************************************************************************/ diff --git a/blakserv/roofile.h b/blakserv/roofile.h index 0d5f062340..071cf16ccc 100644 --- a/blakserv/roofile.h +++ b/blakserv/roofile.h @@ -13,8 +13,6 @@ #ifndef _ROOFILE_H #define _ROOFILE_H -#include "geometry.h" - #pragma region Macros /**************************************************************************************************************/ /* MACROS */ @@ -53,6 +51,7 @@ #define ROUNDROOTOKODFINENESS(a) FINENESSKODTOROO(roundf(FINENESSROOTOKOD(a))) // from blakston.khd, used in BSPGetNextStepTowards across calls +#define ESTATE_LONG_STEP 0x00002000 #define ESTATE_AVOIDING 0x00004000 #define ESTATE_CLOCKWISE 0x00008000 @@ -210,7 +209,9 @@ typedef struct room_type Side* Sides; unsigned short SidesCount; Sector* Sectors; - unsigned short SectorsCount; + unsigned short SectorsCount; + + astar Astar; } room_type; #pragma endregion @@ -219,7 +220,7 @@ typedef struct room_type /* METHODS */ /**************************************************************************************************************/ bool BSPGetHeight(room_type* Room, V2* P, float* HeightF, float* HeightFWD, float* HeightC, BspLeaf** Leaf); -bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall); +bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOutsideBSP, Wall** BlockWall, bool SkipBlockers); bool BSPLineOfSight(room_type* Room, V3* S, V3* E); void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short NewTexture, unsigned int Flags); void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Height, float Speed); diff --git a/kod/include/blakston.khd b/kod/include/blakston.khd index 38a6f84cf5..e0b37dec85 100644 --- a/kod/include/blakston.khd +++ b/kod/include/blakston.khd @@ -1607,6 +1607,7 @@ STATE_MOVE = 0x00010 STATE_ZERO_MASK = 0xFF000 + ESTATE_LONG_STEP = 0x02000 ESTATE_AVOIDING = 0x04000 ESTATE_CLOCKWISE = 0x08000 diff --git a/kod/object/active/holder/nomoveon/battler/monster.kod b/kod/object/active/holder/nomoveon/battler/monster.kod index b2fa4765c2..321b465684 100644 --- a/kod/object/active/holder/nomoveon/battler/monster.kod +++ b/kod/object/active/holder/nomoveon/battler/monster.kod @@ -5352,6 +5352,13 @@ messages: % see formula above iTime = 10000 / (viSpeed * 4); + % diagonal move: scale-up the time according to the increased distance + if piState & ESTATE_LONG_STEP + { + % sqrt(2) = 1.41421356 + iTime = (14142 * iTime) / 10000; + } + % finally we reduce the timer a bit (right now it's exact at ms) % why? the client should get a new position before the last destination % is reached. This prevents cases of stuttering if our message was a From 4b705d7a1074db58e6c9d1ea284881bbb7fd9416 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Thu, 22 Oct 2015 02:42:01 +0200 Subject: [PATCH 02/11] define value for edgescache --- blakserv/astar.c | 8 +++++++- blakserv/astar.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 49833f587b..5f837d882b 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -241,9 +241,11 @@ __inline void AStarAddBlockers(room_type *Room) __inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) { + Wall* blockWall; + +#if EDGESCACHEENABLED bool knows = (((*Node->Edges) & KnowsVal) == KnowsVal); bool can; - Wall* blockWall; // not yet cached if (!knows) @@ -260,6 +262,10 @@ __inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Ne can = (((*Node->Edges) & CanVal) == CanVal); return can; + +#else + return BSPCanMoveInRoom(Room, &Node->Location, &Neighbour->Location, Room->Astar.ObjectID, false, &blockWall, true); +#endif } __inline void AStarProcessNeighbour(room_type* Room, astar_node* EndNode, astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) diff --git a/blakserv/astar.h b/blakserv/astar.h index 2f5f940fe8..25dad01bfe 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -12,6 +12,8 @@ #ifndef _ASTAR_H #define _ASTAR_H +#define EDGESCACHEENABLED 1 + #define CLOSEENOUGHDIST 3 #define DESTBLOCKIGNORE 3 #define ASTARENABLED 1 From bd539d74b7fa841874eefddb2a6a34dcb3363ff3 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Sat, 24 Oct 2015 01:22:08 +0200 Subject: [PATCH 03/11] Path caching --- blakserv/astar.c | 159 +++++++++++++++++++++++++++++++++++++++------ blakserv/astar.h | 36 ++++++++-- blakserv/roofile.c | 14 +++- 3 files changed, 180 insertions(+), 29 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 5f837d882b..ac4a5b85e8 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -408,6 +408,8 @@ void AStarGenerateGrid(room_type* Room) Room->Astar.NodesData = (astar_node_data*)AllocateMemory( MALLOC_ID_ASTAR, Room->Astar.NodesDataSize); + /**********************************************************************/ +#if EDGESCACHEENABLED // allocate memory to cache edges (BSP queries) Room->Astar.EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); @@ -415,21 +417,31 @@ void AStarGenerateGrid(room_type* Room) MALLOC_ID_ASTAR, Room->Astar.EdgesCacheSize); AStarClearEdgesCache(Room); +#endif + /**********************************************************************/ +#if PATHCACHEENABLED + // setup path cache + for (int i = 0; i < PATHCACHESIZE; i++) + Room->Astar.Paths[i] = new astar_path(); +#endif + /**********************************************************************/ // allocate memory for the persistent nodesinfo (typical 2d array by **) Room->Astar.Grid = (astar_node**)AllocateMemory( MALLOC_ID_ASTAR, Room->rowshighres * sizeof(astar_node*)); - // setup rows - for (int i = 0; i < Room->rowshighres; i++) + // allocate rows + for (int i = 0; i < Room->rowshighres; i++) Room->Astar.Grid[i] = (astar_node*)AllocateMemory( - MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node)); + MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node)); + + /**********************************************************************/ // setup all squares for (int i = 0; i < Room->rowshighres; i++) { - for (int j = 0; j < Room->colshighres; j++) - { + for (int j = 0; j < Room->colshighres; j++) + { astar_node* node = &Room->Astar.Grid[i][j]; float f1, f2, f3; node->Row = i; @@ -446,10 +458,10 @@ void AStarGenerateGrid(room_type* Room) // setup reference to data (costs etc.) in erasable mem area node->Data = &Room->Astar.NodesData[idx]; - +#if EDGESCACHEENABLED // setup reference to edgesdata in edgescache node->Edges = &Room->Astar.EdgesCache[idx]; - +#endif // setup neighbour pointers int r, c; @@ -494,21 +506,94 @@ void AStarFreeGrid(room_type* Room) // free workdata mem FreeMemory(MALLOC_ID_ASTAR, Room->Astar.NodesData, Room->Astar.NodesDataSize); +#if EDGESCACHEENABLED // free edgescache mem FreeMemory(MALLOC_ID_ASTAR, Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); +#endif + +#if PATHCACHEENABLED + // free pathes list allocations + for (int i = 0; i < PATHCACHESIZE; i++) + delete Room->Astar.Paths[i]; +#endif // free each row mem - for (int i = 0; i < Room->rowshighres; i++) + for (int i = 0; i < Room->rowshighres; i++) FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid[i], Room->colshighres * sizeof(astar_node)); - + // free rowsmem FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid, Room->rowshighres * sizeof(astar_node*)); } +#if EDGESCACHEENABLED void AStarClearEdgesCache(room_type* Room) { ZeroMemory(Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); } +#endif + +#if PATHCACHEENABLED +void AStarClearPathCache(room_type* Room) +{ + for (int i = 0; i < PATHCACHESIZE; i++) + Room->Astar.Paths[i]->clear(); +} + +bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, unsigned int* Flags, int ObjectID) +{ + Wall* blockWall; + + for (int i = 0; i < PATHCACHESIZE; i++) + { + astar_path* path = Room->Astar.Paths[i]; + + // a valid path would have two entries + if (path->size() < 2) + continue; + + // check start (better match exactly, or we might stepping back?) + astar_node* first = path->front(); + if (first->Row != S->Row || first->Col != S->Col) + continue; + + // check end (has small tolerance) + astar_node* last = path->back(); + if (abs(last->Row - E->Row) > PATHCACHETOLERANCE || + abs(last->Col - E->Col) > PATHCACHETOLERANCE) + continue; + + // match! now remove first, so we also match on next step + path->pop_front(); + + // get the next step endpoint + astar_node* next = path->front(); + + // make sure we can still move to this next endpoint (cares for moved objects!) + // note: if objects block a cached path, the path will still be walked until the block occurs! + // to improve this revalidate the whole path from the first to the last node here + if (!BSPCanMoveInRoom(Room, &first->Location, &next->Location, ObjectID, false, &blockWall, false)) + continue; + + // for diagonal moves mark to be long step (required for timer elapse) + if (abs(first->Col - next->Col) && + abs(first->Row - next->Row)) + { + *Flags |= ESTATE_LONG_STEP; + } + else + *Flags &= ~ESTATE_LONG_STEP; + + // set step endpoint + *P = next->Location; + + // cache hit + return true; + } + + // no cache hit + return false; +} +#endif bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) { @@ -532,6 +617,14 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla astar_node* startnode = &Room->Astar.Grid[startrow][startcol]; Room->Astar.EndNode = &Room->Astar.Grid[endrow][endcol]; + /**********************************************************************/ +#if PATHCACHEENABLED + // first try path cache lookup + if (AStarGetStepFromCache(Room, startnode, Room->Astar.EndNode, P, Flags, ObjectID)) + return true; +#endif + /**********************************************************************/ + // init the astar struct Room->Astar.LastNode = NULL; Room->Astar.ObjectID = ObjectID; @@ -554,21 +647,47 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla //AStarWriteGridToFile(Room); //AStarWriteHeapToFile(Room); - /**********************************************************************/ - // now let's walk back our parent pointers starting from the LastNode - // this chain represents the path (if it was unreachable, it's null) - astar_node* parent = Room->Astar.LastNode; - while (parent && parent->Data->parent != startnode) - parent = parent->Data->parent; + /**********************************************************************/ // unreachable - if (!parent) + if (!Room->Astar.LastNode) return false; - + +#if PATHCACHEENABLED + // check for resetting nextpathidx + if (Room->Astar.NextPathIdx >= PATHCACHESIZE) + Room->Astar.NextPathIdx = 0; + + // get path from cache and clear it + astar_path* path = Room->Astar.Paths[Room->Astar.NextPathIdx]; + Room->Astar.NextPathIdx++; + + // clear old cached path + path->clear(); +#else + // use a temporary ::std:list + astar_path _path; + astar_path* path = &_path; +#endif + + // walk back parent pointers from lastnode (=path) + astar_node* node = Room->Astar.LastNode; + while (node) + { + path->push_front(node); + node = node->Data->parent; + } + + // not interested in first one (=startnode) + path->pop_front(); + + // this is the next one + node = path->front(); + // for diagonal moves mark to be long step (required for timer elapse) - if (abs(parent->Col - startnode->Col) && - abs(parent->Row - startnode->Row)) + if (abs(node->Col - startnode->Col) && + abs(node->Row - startnode->Row)) { *Flags |= ESTATE_LONG_STEP; } @@ -576,7 +695,7 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla *Flags &= ~ESTATE_LONG_STEP; // set step endpoint - *P = parent->Location; + *P = node->Location; return true; } diff --git a/blakserv/astar.h b/blakserv/astar.h index 25dad01bfe..9c61df6424 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -9,15 +9,19 @@ * astar.h: */ +#include + #ifndef _ASTAR_H #define _ASTAR_H -#define EDGESCACHEENABLED 1 - -#define CLOSEENOUGHDIST 3 -#define DESTBLOCKIGNORE 3 -#define ASTARENABLED 1 -#define NUMNEIGHBOURS 8 +#define EDGESCACHEENABLED 1 +#define PATHCACHEENABLED 1 +#define PATHCACHETOLERANCE 3 +#define PATHCACHESIZE 16 +#define CLOSEENOUGHDIST 3 +#define DESTBLOCKIGNORE 3 +#define ASTARENABLED 1 +#define NUMNEIGHBOURS 8 #define LCHILD(x) (2 * x + 1) #define RCHILD(x) (2 * x + 2) @@ -68,15 +72,27 @@ typedef struct astar_node BspLeaf* Leaf; astar_node_data* Data; astar_node* Neighbours[NUMNEIGHBOURS]; +#if EDGESCACHEENABLED unsigned short* Edges; +#endif } astar_node; +class astar_path : public ::std::list +{ +}; + typedef struct astar { astar_node_data* NodesData; int NodesDataSize; +#if EDGESCACHEENABLED unsigned short* EdgesCache; int EdgesCacheSize; +#endif +#if PATHCACHEENABLED + astar_path* Paths[PATHCACHESIZE]; + unsigned int NextPathIdx; +#endif astar_node** Grid; astar_node* EndNode; astar_node* LastNode; @@ -86,7 +102,13 @@ typedef struct astar void AStarGenerateGrid(room_type* Room); void AStarFreeGrid(room_type* Room); -void AStarClearEdgesCache(room_type* Room); bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); +#if EDGESCACHEENABLED +void AStarClearEdgesCache(room_type* Room); +#endif +#if PATHCACHEENABLED +void AStarClearPathCache(room_type* Room); +#endif + #endif /*#ifndef _ASTAR_H */ \ No newline at end of file diff --git a/blakserv/roofile.c b/blakserv/roofile.c index 64c19d26f7..571ece6c1e 100644 --- a/blakserv/roofile.c +++ b/blakserv/roofile.c @@ -759,10 +759,15 @@ void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short New } } - // must invalidate cached astar edges + // must invalidate astar caches #if ASTARENABLED +#if EDGESCACHEENABLED AStarClearEdgesCache(Room); #endif +#if PATHCACHEENABLED + AStarClearPathCache(Room); +#endif +#endif } /*********************************************************************************************/ @@ -794,10 +799,15 @@ void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Hei } } - // must invalidate cached astar edges + // must invalidate astar caches #if ASTARENABLED +#if EDGESCACHEENABLED AStarClearEdgesCache(Room); #endif +#if PATHCACHEENABLED + AStarClearPathCache(Room); +#endif +#endif } /*********************************************************************************************/ From 6c7d94805d92f4a2f691b448773ef28f93398ec6 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Sun, 25 Oct 2015 21:51:46 +0100 Subject: [PATCH 04/11] Refactoring (remove room1:1astar) --- blakserv/astar.c | 28 ++++++++++++++-------------- blakserv/astar.h | 8 -------- blakserv/roofile.h | 9 +++++++++ 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index ac4a5b85e8..5508d1dbdb 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -411,10 +411,10 @@ void AStarGenerateGrid(room_type* Room) /**********************************************************************/ #if EDGESCACHEENABLED // allocate memory to cache edges (BSP queries) - Room->Astar.EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); + Room->EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); - Room->Astar.EdgesCache = (unsigned short*)AllocateMemory( - MALLOC_ID_ASTAR, Room->Astar.EdgesCacheSize); + Room->EdgesCache = (unsigned short*)AllocateMemory( + MALLOC_ID_ASTAR, Room->EdgesCacheSize); AStarClearEdgesCache(Room); #endif @@ -422,7 +422,7 @@ void AStarGenerateGrid(room_type* Room) #if PATHCACHEENABLED // setup path cache for (int i = 0; i < PATHCACHESIZE; i++) - Room->Astar.Paths[i] = new astar_path(); + Room->Paths[i] = new astar_path(); #endif /**********************************************************************/ @@ -460,7 +460,7 @@ void AStarGenerateGrid(room_type* Room) node->Data = &Room->Astar.NodesData[idx]; #if EDGESCACHEENABLED // setup reference to edgesdata in edgescache - node->Edges = &Room->Astar.EdgesCache[idx]; + node->Edges = &Room->EdgesCache[idx]; #endif // setup neighbour pointers int r, c; @@ -508,13 +508,13 @@ void AStarFreeGrid(room_type* Room) #if EDGESCACHEENABLED // free edgescache mem - FreeMemory(MALLOC_ID_ASTAR, Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); + FreeMemory(MALLOC_ID_ASTAR, Room->EdgesCache, Room->EdgesCacheSize); #endif #if PATHCACHEENABLED // free pathes list allocations for (int i = 0; i < PATHCACHESIZE; i++) - delete Room->Astar.Paths[i]; + delete Room->Paths[i]; #endif // free each row mem @@ -528,7 +528,7 @@ void AStarFreeGrid(room_type* Room) #if EDGESCACHEENABLED void AStarClearEdgesCache(room_type* Room) { - ZeroMemory(Room->Astar.EdgesCache, Room->Astar.EdgesCacheSize); + ZeroMemory(Room->EdgesCache, Room->EdgesCacheSize); } #endif @@ -536,7 +536,7 @@ void AStarClearEdgesCache(room_type* Room) void AStarClearPathCache(room_type* Room) { for (int i = 0; i < PATHCACHESIZE; i++) - Room->Astar.Paths[i]->clear(); + Room->Paths[i]->clear(); } bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, unsigned int* Flags, int ObjectID) @@ -545,7 +545,7 @@ bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, for (int i = 0; i < PATHCACHESIZE; i++) { - astar_path* path = Room->Astar.Paths[i]; + astar_path* path = Room->Paths[i]; // a valid path would have two entries if (path->size() < 2) @@ -656,12 +656,12 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla #if PATHCACHEENABLED // check for resetting nextpathidx - if (Room->Astar.NextPathIdx >= PATHCACHESIZE) - Room->Astar.NextPathIdx = 0; + if (Room->NextPathIdx >= PATHCACHESIZE) + Room->NextPathIdx = 0; // get path from cache and clear it - astar_path* path = Room->Astar.Paths[Room->Astar.NextPathIdx]; - Room->Astar.NextPathIdx++; + astar_path* path = Room->Paths[Room->NextPathIdx]; + Room->NextPathIdx++; // clear old cached path path->clear(); diff --git a/blakserv/astar.h b/blakserv/astar.h index 9c61df6424..b56028391f 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -85,14 +85,6 @@ typedef struct astar { astar_node_data* NodesData; int NodesDataSize; -#if EDGESCACHEENABLED - unsigned short* EdgesCache; - int EdgesCacheSize; -#endif -#if PATHCACHEENABLED - astar_path* Paths[PATHCACHESIZE]; - unsigned int NextPathIdx; -#endif astar_node** Grid; astar_node* EndNode; astar_node* LastNode; diff --git a/blakserv/roofile.h b/blakserv/roofile.h index 071cf16ccc..38d22854b7 100644 --- a/blakserv/roofile.h +++ b/blakserv/roofile.h @@ -212,6 +212,15 @@ typedef struct room_type unsigned short SectorsCount; astar Astar; + +#if EDGESCACHEENABLED + unsigned short* EdgesCache; + int EdgesCacheSize; +#endif +#if PATHCACHEENABLED + astar_path* Paths[PATHCACHESIZE]; + unsigned int NextPathIdx; +#endif } room_type; #pragma endregion From ae6be31fc6f1d646754b17c472b14c84aecec044 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Sun, 25 Oct 2015 22:54:19 +0100 Subject: [PATCH 05/11] More refactoring --- blakserv/astar.c | 34 +++++++++++++++++----------------- blakserv/astar.h | 1 - blakserv/roofile.h | 1 + 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 5508d1dbdb..5133577606 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -231,7 +231,7 @@ __inline void AStarAddBlockers(room_type *Room) if (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) continue; - astar_node* node = &Room->Astar.Grid[r][c]; + astar_node* node = &Room->Grid[r][c]; node->Data->isBlocked = true; } } @@ -387,7 +387,7 @@ void AStarWriteGridToFile(room_type* Room) sprintf(rowstring, "Row %3i- ", row); for (int col = 0; col < cols; col++) { - sprintf(rowstring, "%s|%7.3f|", rowstring, Room->Astar.Grid[row][col].Data->combined); + sprintf(rowstring, "%s|%7.3f|", rowstring, Room->Grid[row][col].Data->combined); } sprintf(rowstring, "%s \n", rowstring); fputs(rowstring, fp); @@ -427,12 +427,12 @@ void AStarGenerateGrid(room_type* Room) /**********************************************************************/ // allocate memory for the persistent nodesinfo (typical 2d array by **) - Room->Astar.Grid = (astar_node**)AllocateMemory( + Room->Grid = (astar_node**)AllocateMemory( MALLOC_ID_ASTAR, Room->rowshighres * sizeof(astar_node*)); // allocate rows for (int i = 0; i < Room->rowshighres; i++) - Room->Astar.Grid[i] = (astar_node*)AllocateMemory( + Room->Grid[i] = (astar_node*)AllocateMemory( MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node)); /**********************************************************************/ @@ -442,7 +442,7 @@ void AStarGenerateGrid(room_type* Room) { for (int j = 0; j < Room->colshighres; j++) { - astar_node* node = &Room->Astar.Grid[i][j]; + astar_node* node = &Room->Grid[i][j]; float f1, f2, f3; node->Row = i; node->Col = j; @@ -468,35 +468,35 @@ void AStarGenerateGrid(room_type* Room) // N r = node->Row - 1; c = node->Col + 0; - node->Neighbours[0] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[0] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // NE r = node->Row - 1; c = node->Col + 1; - node->Neighbours[1] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[1] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // E r = node->Row + 0; c = node->Col + 1; - node->Neighbours[2] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[2] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // SE r = node->Row + 1; c = node->Col + 1; - node->Neighbours[3] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[3] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // S r = node->Row + 1; c = node->Col + 0; - node->Neighbours[4] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[4] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // SW r = node->Row + 1; c = node->Col - 1; - node->Neighbours[5] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[5] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // W r = node->Row + 0; c = node->Col - 1; - node->Neighbours[6] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[6] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // NW r = node->Row - 1; c = node->Col - 1; - node->Neighbours[7] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Astar.Grid[r][c]; + node->Neighbours[7] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; } } } @@ -519,10 +519,10 @@ void AStarFreeGrid(room_type* Room) // free each row mem for (int i = 0; i < Room->rowshighres; i++) - FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid[i], Room->colshighres * sizeof(astar_node)); + FreeMemory(MALLOC_ID_ASTAR, Room->Grid[i], Room->colshighres * sizeof(astar_node)); // free rowsmem - FreeMemory(MALLOC_ID_ASTAR, Room->Astar.Grid, Room->rowshighres * sizeof(astar_node*)); + FreeMemory(MALLOC_ID_ASTAR, Room->Grid, Room->rowshighres * sizeof(astar_node*)); } #if EDGESCACHEENABLED @@ -614,8 +614,8 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // get start and endnode - astar_node* startnode = &Room->Astar.Grid[startrow][startcol]; - Room->Astar.EndNode = &Room->Astar.Grid[endrow][endcol]; + astar_node* startnode = &Room->Grid[startrow][startcol]; + Room->Astar.EndNode = &Room->Grid[endrow][endcol]; /**********************************************************************/ #if PATHCACHEENABLED diff --git a/blakserv/astar.h b/blakserv/astar.h index b56028391f..127a97f0f7 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -85,7 +85,6 @@ typedef struct astar { astar_node_data* NodesData; int NodesDataSize; - astar_node** Grid; astar_node* EndNode; astar_node* LastNode; int ObjectID; diff --git a/blakserv/roofile.h b/blakserv/roofile.h index 38d22854b7..c1c9874890 100644 --- a/blakserv/roofile.h +++ b/blakserv/roofile.h @@ -213,6 +213,7 @@ typedef struct room_type astar Astar; + astar_node** Grid; #if EDGESCACHEENABLED unsigned short* EdgesCache; int EdgesCacheSize; From b065cca731a74f5328824612e779772e42e9296b Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Mon, 26 Oct 2015 02:08:19 +0100 Subject: [PATCH 06/11] more astar refactoring --- blakserv/astar.c | 208 +++++++++++++++++++++++++-------------------- blakserv/astar.h | 8 ++ blakserv/main.c | 2 + blakserv/roofile.h | 2 - 4 files changed, 126 insertions(+), 94 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 5133577606..cab2ef42cf 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -20,27 +20,29 @@ Improvements over the old implementation: #include "blakserv.h" +astar AStar; + #pragma region HEAP -// refers to the i-th element on the heap of a room -#define HEAP(r, i) ((r)->Astar.NodesData[i].heapslot) +// refers to the i-th element on the heap +#define HEAP(i) (AStar.NodesData[i].heapslot) __inline bool AStarHeapCheck(room_type* Room, int i) { - int squares = Room->colshighres * Room->rowshighres; + int squares = Room->rowshighres * Room->colshighres; if (i >= squares) return true; - if (!HEAP(Room, i)) + if (!HEAP(i)) return true; - float our = HEAP(Room, i)->Data->combined; + float our = HEAP(i)->Data->combined; int lchild = LCHILD(i); int rchild = RCHILD(i); if (lchild < squares) { - astar_node* node = HEAP(Room, lchild); + astar_node* node = HEAP(lchild); if (node) { if (node->Data->combined < our) @@ -49,7 +51,7 @@ __inline bool AStarHeapCheck(room_type* Room, int i) } if (rchild < squares) { - astar_node* node = HEAP(Room, rchild); + astar_node* node = HEAP(rchild); if (node) { if (node->Data->combined < our) @@ -73,7 +75,7 @@ __inline void AStarWriteHeapToFile(room_type* Room) if (fp) { int maxlayer = 9; - int treesize = Room->colshighres * Room->rowshighres; + int treesize = cols * rows; int rangestart = 0; int rangesize = 1; for (int i = 1; i < maxlayer; i++) @@ -85,7 +87,7 @@ __inline void AStarWriteHeapToFile(room_type* Room) for (int k = 0; k < rangesize; k++) { - astar_node* node = Room->Astar.NodesData[rangestart + k].heapslot; + astar_node* node = AStar.NodesData[rangestart + k].heapslot; if (node) sprintf(rowstring, "%s|%6.2f|", rowstring, node->Data->combined); @@ -105,103 +107,103 @@ __inline void AStarWriteHeapToFile(room_type* Room) FreeMemory(MALLOC_ID_ROOM, rowstring, 60000); } -__inline void AStarHeapSwap(room_type* Room, int idx1, int idx2) +__inline void AStarHeapSwap(int idx1, int idx2) { - astar_node* node1 = HEAP(Room, idx1); - astar_node* node2 = HEAP(Room, idx2); - HEAP(Room, idx1) = node2; - HEAP(Room, idx2) = node1; + astar_node* node1 = HEAP(idx1); + astar_node* node2 = HEAP(idx2); + HEAP(idx1) = node2; + HEAP(idx2) = node1; node1->Data->heapindex = idx2; node2->Data->heapindex = idx1; } -__inline void AStarHeapMoveUp(room_type* Room, int Index) +__inline void AStarHeapMoveUp(int Index) { int i = Index; - while (i > 0 && HEAP(Room, i)->Data->combined < HEAP(Room, PARENT(i))->Data->combined) + while (i > 0 && HEAP(i)->Data->combined < HEAP(PARENT(i))->Data->combined) { - AStarHeapSwap(Room, i, PARENT(i)); + AStarHeapSwap(i, PARENT(i)); i = PARENT(i); } } -__inline void AStarHeapHeapify(room_type* Room, int Index) +__inline void AStarHeapHeapify(int Index) { int i = Index; do { int min = i; - if (HEAP(Room, LCHILD(i)) && HEAP(Room, LCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined) + if (HEAP(LCHILD(i)) && HEAP(LCHILD(i))->Data->combined < HEAP(min)->Data->combined) min = LCHILD(i); - if (HEAP(Room, RCHILD(i)) && HEAP(Room, RCHILD(i))->Data->combined < HEAP(Room, min)->Data->combined) + if (HEAP(RCHILD(i)) && HEAP(RCHILD(i))->Data->combined < HEAP(min)->Data->combined) min = RCHILD(i); if (min == i) break; - AStarHeapSwap(Room, i, min); + AStarHeapSwap(i, min); i = min; } while (true); } -__inline void AStarHeapInsert(room_type* Room, astar_node* Node) +__inline void AStarHeapInsert(astar_node* Node) { // save index of this node - Node->Data->heapindex = Room->Astar.HeapSize; + Node->Data->heapindex = AStar.HeapSize; // add node at the end - HEAP(Room, Node->Data->heapindex) = Node; + HEAP(Node->Data->heapindex) = Node; // increment - Room->Astar.HeapSize++; + AStar.HeapSize++; // push node up until in place - AStarHeapMoveUp(Room, Node->Data->heapindex); + AStarHeapMoveUp(Node->Data->heapindex); } -__inline void AStarHeapRemoveFirst(room_type* Room) +__inline void AStarHeapRemoveFirst() { - int lastIdx = Room->Astar.HeapSize - 1; + int lastIdx = AStar.HeapSize - 1; // no elements if (lastIdx < 0) return; // decrement size - Room->Astar.HeapSize--; + AStar.HeapSize--; // more than 1 if (lastIdx > 0) { // put last at root - AStarHeapSwap(Room, 0, lastIdx); + AStarHeapSwap(0, lastIdx); // zero out the previous root at swapped slot - HEAP(Room, lastIdx)->Data->heapindex = 0; - HEAP(Room, lastIdx) = NULL; + HEAP(lastIdx)->Data->heapindex = 0; + HEAP(lastIdx) = NULL; // reorder tree - AStarHeapHeapify(Room, 0); + AStarHeapHeapify(0); } // only one, clear head else { - HEAP(Room, 0)->Data->heapindex = 0; - HEAP(Room, 0) = NULL; + HEAP(0)->Data->heapindex = 0; + HEAP(0) = NULL; } } #pragma endregion #pragma region ASTAR -__inline void AStarAddBlockers(room_type *Room) +__inline void AStarAddBlockers() { Blocker* b; - b = Room->Blocker; + b = AStar.Room->Blocker; while (b) { // Don't add self. - if (b->ObjectID == Room->Astar.ObjectID) + if (b->ObjectID == AStar.ObjectID) { b = b->Next; continue; @@ -212,13 +214,14 @@ __inline void AStarAddBlockers(room_type *Room) int col = (int)roundf(b->Position.X / 256.0f); // Don't add blockers at the target coords. - if (abs(row - Room->Astar.EndNode->Row) < DESTBLOCKIGNORE && - abs(col - Room->Astar.EndNode->Col) < DESTBLOCKIGNORE) + if (abs(row - AStar.EndNode->Row) < DESTBLOCKIGNORE && + abs(col - AStar.EndNode->Col) < DESTBLOCKIGNORE) { b = b->Next; continue; } + //todo: use neighbour[] ptrs here? // Mark these nodes in A* grid blocked (our coord and +2 highres each dir) for (int rowoffset = -2; rowoffset < 3; rowoffset++) { @@ -228,10 +231,10 @@ __inline void AStarAddBlockers(room_type *Room) int c = col + coloffset; // outside - if (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) + if (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) continue; - astar_node* node = &Room->Grid[r][c]; + astar_node* node = &AStar.Room->Grid[r][c]; node->Data->isBlocked = true; } } @@ -239,7 +242,7 @@ __inline void AStarAddBlockers(room_type *Room) } } -__inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) +__inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) { Wall* blockWall; @@ -251,7 +254,7 @@ __inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Ne if (!knows) { // bsp query - can = BSPCanMoveInRoom(Room, &Node->Location, &Neighbour->Location, Room->Astar.ObjectID, false, &blockWall, true); + can = BSPCanMoveInRoom(AStar.Room, &Node->Location, &Neighbour->Location, AStar.ObjectID, false, &blockWall, true); // save to cache *Node->Edges |= (can) ? CanVal : KnowsVal; @@ -264,26 +267,26 @@ __inline bool AStarCanWalkEdge(room_type* Room, astar_node* Node, astar_node* Ne return can; #else - return BSPCanMoveInRoom(Room, &Node->Location, &Neighbour->Location, Room->Astar.ObjectID, false, &blockWall, true); + return BSPCanMoveInRoom(AStar.Room, &Node->Location, &Neighbour->Location, AStar.ObjectID, false, &blockWall, true); #endif } -__inline void AStarProcessNeighbour(room_type* Room, astar_node* EndNode, astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) +__inline void AStarProcessNeighbour(astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) { // skip any neighbour outside of grid or sector, already processed or blocked if (!Neighbour || !Neighbour->Leaf || Neighbour->Data->isInClosedList || Neighbour->Data->isBlocked) return; // can't walk edge from node to neighbour - if (!AStarCanWalkEdge(Room, Node, Neighbour, KnowsVal, CanVal)) + if (!AStarCanWalkEdge(Node, Neighbour, KnowsVal, CanVal)) return; // heuristic (~ estimated distance from node to end) // need to do this only once if (Neighbour->Data->heuristic == 0.0f) { - float dx = (float)abs(Neighbour->Col - EndNode->Col); - float dy = (float)abs(Neighbour->Row - EndNode->Row); + float dx = (float)abs(Neighbour->Col - AStar.EndNode->Col); + float dy = (float)abs(Neighbour->Row - AStar.EndNode->Row); // octile-distance Neighbour->Data->heuristic = @@ -303,7 +306,7 @@ __inline void AStarProcessNeighbour(room_type* Room, astar_node* EndNode, astar_ Neighbour->Data->combined = Neighbour->Data->cost + Neighbour->Data->heuristic; // add it sorted to the open list - AStarHeapInsert(Room, Neighbour); + AStarHeapInsert(Neighbour); } // CASE 2) @@ -324,16 +327,16 @@ __inline void AStarProcessNeighbour(room_type* Room, astar_node* EndNode, astar_ // reorder it upwards in the heap tree, don't care about downordering // since costs are lower and heuristic is always the same, // it's guaranteed to be moved up - AStarHeapMoveUp(Room, Neighbour->Data->heapindex); + AStarHeapMoveUp(Neighbour->Data->heapindex); } } } -__inline bool AStarProcessFirst(room_type* Room) +__inline bool AStarProcessFirst() { // shortcuts - astar_node* node = HEAP(Room, 0); - astar_node* endNode = Room->Astar.EndNode; + astar_node* node = HEAP(0); + astar_node* endNode = AStar.EndNode; // openlist empty, unreachable if (!node) @@ -343,23 +346,23 @@ __inline bool AStarProcessFirst(room_type* Room) if (abs(node->Col - endNode->Col) < CLOSEENOUGHDIST && abs(node->Row - endNode->Row) < CLOSEENOUGHDIST) { - Room->Astar.LastNode = node; + AStar.LastNode = node; return false; } /****************************************************************/ // straight neighbours - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[0], COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[2], COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[4], COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[6], COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); + AStarProcessNeighbour(node, node->Neighbours[0], COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); + AStarProcessNeighbour(node, node->Neighbours[2], COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); + AStarProcessNeighbour(node, node->Neighbours[4], COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); + AStarProcessNeighbour(node, node->Neighbours[6], COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); // diagonal neighbours - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[1], COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[3], COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[5], COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); - AStarProcessNeighbour(Room, endNode, node, node->Neighbours[7], COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); + AStarProcessNeighbour(node, node->Neighbours[1], COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); + AStarProcessNeighbour(node, node->Neighbours[3], COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); + AStarProcessNeighbour(node, node->Neighbours[5], COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); + AStarProcessNeighbour(node, node->Neighbours[7], COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); /****************************************************************/ @@ -367,7 +370,7 @@ __inline bool AStarProcessFirst(room_type* Room) node->Data->isInClosedList = true; // remove it from the open list - AStarHeapRemoveFirst(Room); + AStarHeapRemoveFirst(); return true; } @@ -400,15 +403,12 @@ void AStarWriteGridToFile(room_type* Room) void AStarGenerateGrid(room_type* Room) { - // note: we allocate all memory for the astar_node_data of all squares together - // this is necessary so we can erase it with a single cheap ZeroMemory call.. - Room->Astar.NodesDataSize = Room->colshighres * Room->rowshighres * sizeof(astar_node_data); - - // allocate memory for all nodesdata (cleaned for each calculation) - Room->Astar.NodesData = (astar_node_data*)AllocateMemory( - MALLOC_ID_ASTAR, Room->Astar.NodesDataSize); + int squares = Room->colshighres * Room->rowshighres; + if (squares > NODESDATASQUARES) + eprintf("ALERT!! You tried to load a room with a grid-size beyond allowed limits! AStar will fail."); /**********************************************************************/ + #if EDGESCACHEENABLED // allocate memory to cache edges (BSP queries) Room->EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); @@ -456,8 +456,13 @@ void AStarGenerateGrid(room_type* Room) int idx = i*Room->colshighres + j; + // prevent setting up a data-pointer into invalid data mem + // instead map to first datanode, see error log in AStarGenerateGrid + if (idx >= NODESDATASQUARES) + idx = 0; + // setup reference to data (costs etc.) in erasable mem area - node->Data = &Room->Astar.NodesData[idx]; + node->Data = &AStar.NodesData[idx]; #if EDGESCACHEENABLED // setup reference to edgesdata in edgescache node->Edges = &Room->EdgesCache[idx]; @@ -503,9 +508,6 @@ void AStarGenerateGrid(room_type* Room) void AStarFreeGrid(room_type* Room) { - // free workdata mem - FreeMemory(MALLOC_ID_ASTAR, Room->Astar.NodesData, Room->Astar.NodesDataSize); - #if EDGESCACHEENABLED // free edgescache mem FreeMemory(MALLOC_ID_ASTAR, Room->EdgesCache, Room->EdgesCacheSize); @@ -613,37 +615,36 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ - // get start and endnode - astar_node* startnode = &Room->Grid[startrow][startcol]; - Room->Astar.EndNode = &Room->Grid[endrow][endcol]; + // set data on astar + AStar.StartNode = &Room->Grid[startrow][startcol]; + AStar.EndNode = &Room->Grid[endrow][endcol]; + AStar.Room = Room; + AStar.LastNode = NULL; + AStar.ObjectID = ObjectID; + AStar.HeapSize = 0; /**********************************************************************/ #if PATHCACHEENABLED // first try path cache lookup - if (AStarGetStepFromCache(Room, startnode, Room->Astar.EndNode, P, Flags, ObjectID)) + if (AStarGetStepFromCache(Room, AStar.StartNode, AStar.EndNode, P, Flags, ObjectID)) return true; #endif /**********************************************************************/ - // init the astar struct - Room->Astar.LastNode = NULL; - Room->Astar.ObjectID = ObjectID; - Room->Astar.HeapSize = 0; - // prepare non-persistent astar grid data memory - ZeroMemory(Room->Astar.NodesData, Room->Astar.NodesDataSize); + ZeroMemory(AStar.NodesData, AStar.NodesDataSize); /**********************************************************************/ // mark nodes blocked by objects - AStarAddBlockers(Room); + AStarAddBlockers(); // insert startnode into heap-tree - AStarHeapInsert(Room, startnode); + AStarHeapInsert(AStar.StartNode); // the algorithm finishes if we either hit a node close enough to endnode // or if there is no more entries in the open list (unreachable) - while (AStarProcessFirst(Room)) {} + while (AStarProcessFirst()) {} //AStarWriteGridToFile(Room); //AStarWriteHeapToFile(Room); @@ -651,7 +652,7 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // unreachable - if (!Room->Astar.LastNode) + if (!AStar.LastNode) return false; #if PATHCACHEENABLED @@ -672,7 +673,7 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla #endif // walk back parent pointers from lastnode (=path) - astar_node* node = Room->Astar.LastNode; + astar_node* node = AStar.LastNode; while (node) { path->push_front(node); @@ -686,8 +687,8 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla node = path->front(); // for diagonal moves mark to be long step (required for timer elapse) - if (abs(node->Col - startnode->Col) && - abs(node->Row - startnode->Row)) + if (abs(node->Col - AStar.StartNode->Col) && + abs(node->Row - AStar.StartNode->Row)) { *Flags |= ESTATE_LONG_STEP; } @@ -699,4 +700,27 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla return true; } + +void AStarInit() +{ + // memory for astar data erased with each query + AStar.NodesDataSize = NODESDATASQUARES * sizeof(astar_node_data); + + // allocate memory for all nodesdata (cleaned for each calculation) + AStar.NodesData = (astar_node_data*)AllocateMemory( + MALLOC_ID_ASTAR, AStar.NodesDataSize); + + AStar.StartNode = NULL; + AStar.EndNode = NULL; + AStar.LastNode = NULL; + AStar.ObjectID = 0; + AStar.HeapSize = 0; + AStar.Room = NULL; +} + +void AStarShutdown() +{ + // free workdata mem + FreeMemory(MALLOC_ID_ASTAR, AStar.NodesData, AStar.NodesDataSize); +} #pragma endregion diff --git a/blakserv/astar.h b/blakserv/astar.h index 127a97f0f7..8dd51c133e 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -23,6 +23,10 @@ #define ASTARENABLED 1 #define NUMNEIGHBOURS 8 +// biggest known grid so far is desertshore3.roo +// having 391.680 squares. +#define NODESDATASQUARES 1024 * 1024 + #define LCHILD(x) (2 * x + 1) #define RCHILD(x) (2 * x + 2) #define PARENT(x) ((x-1) / 2) @@ -85,12 +89,16 @@ typedef struct astar { astar_node_data* NodesData; int NodesDataSize; + astar_node* StartNode; astar_node* EndNode; astar_node* LastNode; int ObjectID; int HeapSize; + room_type* Room; } astar; +void AStarInit(); +void AStarShutdown(); void AStarGenerateGrid(room_type* Room); void AStarFreeGrid(room_type* Room); bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); diff --git a/blakserv/main.c b/blakserv/main.c index eb3744b9b0..a5aae0c500 100644 --- a/blakserv/main.c +++ b/blakserv/main.c @@ -83,6 +83,7 @@ void MainServer() InitTimer(); InitSession(); InitResource(); + AStarInit(); InitRooms(); InitString(); InitUser(); @@ -158,6 +159,7 @@ void MainExitServer() ResetString(); // ExitRooms calls ResetRooms in addition to clearing the array memory. ExitRooms(); + AStarShutdown(); ResetResource(); ResetTimer(); ResetList(); diff --git a/blakserv/roofile.h b/blakserv/roofile.h index c1c9874890..2724a818bb 100644 --- a/blakserv/roofile.h +++ b/blakserv/roofile.h @@ -211,8 +211,6 @@ typedef struct room_type Sector* Sectors; unsigned short SectorsCount; - astar Astar; - astar_node** Grid; #if EDGESCACHEENABLED unsigned short* EdgesCache; From 2e97455cf29e9b68866bef316a1673f11d83b991 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Tue, 27 Oct 2015 21:17:13 +0100 Subject: [PATCH 07/11] meta grid/less mem --- blakserv/astar.c | 149 ++++++++++++++++++++++++++++------------------- blakserv/astar.h | 14 ++++- 2 files changed, 99 insertions(+), 64 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index cab2ef42cf..40159600b4 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -214,8 +214,8 @@ __inline void AStarAddBlockers() int col = (int)roundf(b->Position.X / 256.0f); // Don't add blockers at the target coords. - if (abs(row - AStar.EndNode->Row) < DESTBLOCKIGNORE && - abs(col - AStar.EndNode->Col) < DESTBLOCKIGNORE) + if (abs(row - AStar.EndNode->Meta->Row) < DESTBLOCKIGNORE && + abs(col - AStar.EndNode->Meta->Col) < DESTBLOCKIGNORE) { b = b->Next; continue; @@ -254,7 +254,7 @@ __inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned if (!knows) { // bsp query - can = BSPCanMoveInRoom(AStar.Room, &Node->Location, &Neighbour->Location, AStar.ObjectID, false, &blockWall, true); + can = BSPCanMoveInRoom(AStar.Room, &Node->Meta->Location, &Neighbour->Meta->Location, AStar.ObjectID, false, &blockWall, true); // save to cache *Node->Edges |= (can) ? CanVal : KnowsVal; @@ -267,7 +267,7 @@ __inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned return can; #else - return BSPCanMoveInRoom(AStar.Room, &Node->Location, &Neighbour->Location, AStar.ObjectID, false, &blockWall, true); + return BSPCanMoveInRoom(AStar.Room, &Node->Meta->Location, &Neighbour->Meta->Location, AStar.ObjectID, false, &blockWall, true); #endif } @@ -285,8 +285,8 @@ __inline void AStarProcessNeighbour(astar_node* Node, astar_node* Neighbour, flo // need to do this only once if (Neighbour->Data->heuristic == 0.0f) { - float dx = (float)abs(Neighbour->Col - AStar.EndNode->Col); - float dy = (float)abs(Neighbour->Row - AStar.EndNode->Row); + float dx = (float)abs(Neighbour->Meta->Col - AStar.EndNode->Meta->Col); + float dy = (float)abs(Neighbour->Meta->Row - AStar.EndNode->Meta->Row); // octile-distance Neighbour->Data->heuristic = @@ -343,8 +343,8 @@ __inline bool AStarProcessFirst() return false; // close enough, we're done, path found - if (abs(node->Col - endNode->Col) < CLOSEENOUGHDIST && - abs(node->Row - endNode->Row) < CLOSEENOUGHDIST) + if (abs(node->Meta->Col - endNode->Meta->Col) < CLOSEENOUGHDIST && + abs(node->Meta->Row - endNode->Meta->Row) < CLOSEENOUGHDIST) { AStar.LastNode = node; return false; @@ -403,8 +403,7 @@ void AStarWriteGridToFile(room_type* Room) void AStarGenerateGrid(room_type* Room) { - int squares = Room->colshighres * Room->rowshighres; - if (squares > NODESDATASQUARES) + if (Room->colshighres > MAXGRIDCOLS || Room->rowshighres > MAXGRIDROWS) eprintf("ALERT!! You tried to load a room with a grid-size beyond allowed limits! AStar will fail."); /**********************************************************************/ @@ -440,67 +439,64 @@ void AStarGenerateGrid(room_type* Room) // setup all squares for (int i = 0; i < Room->rowshighres; i++) { + int row = (i < MAXGRIDROWS) ? i : 0; + for (int j = 0; j < Room->colshighres; j++) { - astar_node* node = &Room->Grid[i][j]; - float f1, f2, f3; - node->Row = i; - node->Col = j; - - // floatingpoint coordinates of the center of the square in ROO fineness (for queries) - node->Location.X = j * 256.0f;// +128.0f; - node->Location.Y = i * 256.0f;// +128.0f; - - // mark if this square is inside or outside of room - BSPGetHeight(Room, &node->Location, &f1, &f2, &f3, &node->Leaf); + int col = (j < MAXGRIDCOLS) ? j : 0; + int idx1 = row*Room->colshighres + col; + int idx2 = i*Room->colshighres + j; - int idx = i*Room->colshighres + j; - - // prevent setting up a data-pointer into invalid data mem - // instead map to first datanode, see error log in AStarGenerateGrid - if (idx >= NODESDATASQUARES) - idx = 0; + astar_node* node = &Room->Grid[i][j]; // setup reference to data (costs etc.) in erasable mem area - node->Data = &AStar.NodesData[idx]; + node->Data = &AStar.NodesData[idx1]; + node->Meta = &AStar.Grid[row][col]; + #if EDGESCACHEENABLED // setup reference to edgesdata in edgescache - node->Edges = &Room->EdgesCache[idx]; + node->Edges = &Room->EdgesCache[idx2]; #endif - // setup neighbour pointers - int r, c; + + float f1, f2, f3; + + // mark if this square is inside or outside of room + BSPGetHeight(Room, &node->Meta->Location, &f1, &f2, &f3, &node->Leaf); + + // setup neighbour pointers + int r, c; // N - r = node->Row - 1; - c = node->Col + 0; + r = node->Meta->Row - 1; + c = node->Meta->Col + 0; node->Neighbours[0] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // NE - r = node->Row - 1; - c = node->Col + 1; + r = node->Meta->Row - 1; + c = node->Meta->Col + 1; node->Neighbours[1] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // E - r = node->Row + 0; - c = node->Col + 1; + r = node->Meta->Row + 0; + c = node->Meta->Col + 1; node->Neighbours[2] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // SE - r = node->Row + 1; - c = node->Col + 1; + r = node->Meta->Row + 1; + c = node->Meta->Col + 1; node->Neighbours[3] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // S - r = node->Row + 1; - c = node->Col + 0; + r = node->Meta->Row + 1; + c = node->Meta->Col + 0; node->Neighbours[4] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // SW - r = node->Row + 1; - c = node->Col - 1; + r = node->Meta->Row + 1; + c = node->Meta->Col - 1; node->Neighbours[5] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // W - r = node->Row + 0; - c = node->Col - 1; + r = node->Meta->Row + 0; + c = node->Meta->Col - 1; node->Neighbours[6] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; // NW - r = node->Row - 1; - c = node->Col - 1; + r = node->Meta->Row - 1; + c = node->Meta->Col - 1; node->Neighbours[7] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; } } @@ -555,13 +551,13 @@ bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, // check start (better match exactly, or we might stepping back?) astar_node* first = path->front(); - if (first->Row != S->Row || first->Col != S->Col) + if (first->Meta->Row != S->Meta->Row || first->Meta->Col != S->Meta->Col) continue; // check end (has small tolerance) astar_node* last = path->back(); - if (abs(last->Row - E->Row) > PATHCACHETOLERANCE || - abs(last->Col - E->Col) > PATHCACHETOLERANCE) + if (abs(last->Meta->Row - E->Meta->Row) > PATHCACHETOLERANCE || + abs(last->Meta->Col - E->Meta->Col) > PATHCACHETOLERANCE) continue; // match! now remove first, so we also match on next step @@ -573,12 +569,12 @@ bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, // make sure we can still move to this next endpoint (cares for moved objects!) // note: if objects block a cached path, the path will still be walked until the block occurs! // to improve this revalidate the whole path from the first to the last node here - if (!BSPCanMoveInRoom(Room, &first->Location, &next->Location, ObjectID, false, &blockWall, false)) + if (!BSPCanMoveInRoom(Room, &first->Meta->Location, &next->Meta->Location, ObjectID, false, &blockWall, false)) continue; // for diagonal moves mark to be long step (required for timer elapse) - if (abs(first->Col - next->Col) && - abs(first->Row - next->Row)) + if (abs(first->Meta->Col - next->Meta->Col) && + abs(first->Meta->Row - next->Meta->Row)) { *Flags |= ESTATE_LONG_STEP; } @@ -586,7 +582,7 @@ bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, *Flags &= ~ESTATE_LONG_STEP; // set step endpoint - *P = next->Location; + *P = next->Meta->Location; // cache hit return true; @@ -632,6 +628,7 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // prepare non-persistent astar grid data memory + // todo: clear only required area? ZeroMemory(AStar.NodesData, AStar.NodesDataSize); /**********************************************************************/ @@ -687,8 +684,8 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla node = path->front(); // for diagonal moves mark to be long step (required for timer elapse) - if (abs(node->Col - AStar.StartNode->Col) && - abs(node->Row - AStar.StartNode->Row)) + if (abs(node->Meta->Col - AStar.StartNode->Meta->Col) && + abs(node->Meta->Row - AStar.StartNode->Meta->Row)) { *Flags |= ESTATE_LONG_STEP; } @@ -696,7 +693,7 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla *Flags &= ~ESTATE_LONG_STEP; // set step endpoint - *P = node->Location; + *P = node->Meta->Location; return true; } @@ -705,10 +702,8 @@ void AStarInit() { // memory for astar data erased with each query AStar.NodesDataSize = NODESDATASQUARES * sizeof(astar_node_data); - - // allocate memory for all nodesdata (cleaned for each calculation) AStar.NodesData = (astar_node_data*)AllocateMemory( - MALLOC_ID_ASTAR, AStar.NodesDataSize); + MALLOC_ID_ASTAR, AStar.NodesDataSize); AStar.StartNode = NULL; AStar.EndNode = NULL; @@ -716,11 +711,43 @@ void AStarInit() AStar.ObjectID = 0; AStar.HeapSize = 0; AStar.Room = NULL; + + // allocate memory for the persistent grid + AStar.Grid = (astar_node_meta**)AllocateMemory(MALLOC_ID_ASTAR, MAXGRIDROWS * sizeof(astar_node_meta*)); + + // allocate rows + for (int i = 0; i < MAXGRIDROWS; i++) + AStar.Grid[i] = (astar_node_meta*)AllocateMemory(MALLOC_ID_ASTAR, MAXGRIDCOLS * sizeof(astar_node_meta)); + + // setup all squares + for (int i = 0; i < MAXGRIDROWS; i++) + { + for (int j = 0; j < MAXGRIDCOLS; j++) + { + int idx = i*MAXGRIDCOLS + j; + + astar_node_meta* node = &AStar.Grid[i][j]; + + // set row/col + node->Row = i; + node->Col = j; + + // floatingpoint coordinates of the center of the square in ROO fineness (for queries) + node->Location.X = j * 256.0f;// +128.0f; + node->Location.Y = i * 256.0f;// +128.0f; + } + } } void AStarShutdown() { - // free workdata mem FreeMemory(MALLOC_ID_ASTAR, AStar.NodesData, AStar.NodesDataSize); + + // free each meta grid row + for (int i = 0; i < MAXGRIDROWS; i++) + FreeMemory(MALLOC_ID_ASTAR, AStar.Grid[i], MAXGRIDCOLS * sizeof(astar_node_meta)); + + // free meta grid + FreeMemory(MALLOC_ID_ASTAR, AStar.Grid, MAXGRIDROWS * sizeof(astar_node_meta*)); } #pragma endregion diff --git a/blakserv/astar.h b/blakserv/astar.h index 8dd51c133e..505b705801 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -25,7 +25,9 @@ // biggest known grid so far is desertshore3.roo // having 391.680 squares. -#define NODESDATASQUARES 1024 * 1024 +#define MAXGRIDROWS 1024 +#define MAXGRIDCOLS 1024 +#define NODESDATASQUARES MAXGRIDROWS * MAXGRIDCOLS #define LCHILD(x) (2 * x + 1) #define RCHILD(x) (2 * x + 2) @@ -68,13 +70,18 @@ typedef struct astar_node_data bool isBlocked; } astar_node_data; -typedef struct astar_node +typedef struct astar_node_meta { int Row; int Col; V2 Location; - BspLeaf* Leaf; +} astar_node_meta; + +typedef struct astar_node +{ + astar_node_meta* Meta; astar_node_data* Data; + BspLeaf* Leaf; astar_node* Neighbours[NUMNEIGHBOURS]; #if EDGESCACHEENABLED unsigned short* Edges; @@ -89,6 +96,7 @@ typedef struct astar { astar_node_data* NodesData; int NodesDataSize; + astar_node_meta** Grid; astar_node* StartNode; astar_node* EndNode; astar_node* LastNode; From 96ee9d9f82628405c8e76bca1dfba7d513c7c967 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Tue, 27 Oct 2015 22:56:10 +0100 Subject: [PATCH 08/11] no neighbour ptr --- blakserv/astar.c | 97 ++++++++++++++++++++++++++---------------------- blakserv/astar.h | 1 - 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 40159600b4..781c910f8a 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -352,17 +352,60 @@ __inline bool AStarProcessFirst() /****************************************************************/ + int r, c; + astar_node* n; + // straight neighbours - AStarProcessNeighbour(node, node->Neighbours[0], COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); - AStarProcessNeighbour(node, node->Neighbours[2], COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); - AStarProcessNeighbour(node, node->Neighbours[4], COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); - AStarProcessNeighbour(node, node->Neighbours[6], COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); + + // n + r = node->Meta->Row - 1; + c = node->Meta->Col + 0; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); + + // e + r = node->Meta->Row + 0; + c = node->Meta->Col + 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); + + // s + r = node->Meta->Row + 1; + c = node->Meta->Col + 0; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); + + // w + r = node->Meta->Row + 0; + c = node->Meta->Col - 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); // diagonal neighbours - AStarProcessNeighbour(node, node->Neighbours[1], COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); - AStarProcessNeighbour(node, node->Neighbours[3], COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); - AStarProcessNeighbour(node, node->Neighbours[5], COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); - AStarProcessNeighbour(node, node->Neighbours[7], COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); + + // ne + r = node->Meta->Row - 1; + c = node->Meta->Col + 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); + + // se + r = node->Meta->Row + 1; + c = node->Meta->Col + 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); + + // sw + r = node->Meta->Row + 1; + c = node->Meta->Col - 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); + + // nw + r = node->Meta->Row - 1; + c = node->Meta->Col - 1; + n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; + AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); /****************************************************************/ @@ -462,42 +505,6 @@ void AStarGenerateGrid(room_type* Room) // mark if this square is inside or outside of room BSPGetHeight(Room, &node->Meta->Location, &f1, &f2, &f3, &node->Leaf); - - // setup neighbour pointers - int r, c; - - // N - r = node->Meta->Row - 1; - c = node->Meta->Col + 0; - node->Neighbours[0] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // NE - r = node->Meta->Row - 1; - c = node->Meta->Col + 1; - node->Neighbours[1] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // E - r = node->Meta->Row + 0; - c = node->Meta->Col + 1; - node->Neighbours[2] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // SE - r = node->Meta->Row + 1; - c = node->Meta->Col + 1; - node->Neighbours[3] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // S - r = node->Meta->Row + 1; - c = node->Meta->Col + 0; - node->Neighbours[4] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // SW - r = node->Meta->Row + 1; - c = node->Meta->Col - 1; - node->Neighbours[5] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // W - r = node->Meta->Row + 0; - c = node->Meta->Col - 1; - node->Neighbours[6] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; - // NW - r = node->Meta->Row - 1; - c = node->Meta->Col - 1; - node->Neighbours[7] = (r < 0 || c < 0 || r >= Room->rowshighres || c >= Room->colshighres) ? NULL : &Room->Grid[r][c]; } } } @@ -628,8 +635,8 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // prepare non-persistent astar grid data memory - // todo: clear only required area? ZeroMemory(AStar.NodesData, AStar.NodesDataSize); + //ZeroMemory(AStar.NodesData, Room->rowshighres * Room->colshighres * sizeof(astar_node_data)); /**********************************************************************/ diff --git a/blakserv/astar.h b/blakserv/astar.h index 505b705801..bcb32a28fc 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -82,7 +82,6 @@ typedef struct astar_node astar_node_meta* Meta; astar_node_data* Data; BspLeaf* Leaf; - astar_node* Neighbours[NUMNEIGHBOURS]; #if EDGESCACHEENABLED unsigned short* Edges; #endif From 3d0f86c79ada36d83e585ba7ff194a682c12b3b9 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Tue, 27 Oct 2015 23:10:03 +0100 Subject: [PATCH 09/11] mem usage output formatting astar --- blakserv/adminfn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blakserv/adminfn.c b/blakserv/adminfn.c index 0e0d069d83..4ad5655ef5 100644 --- a/blakserv/adminfn.c +++ b/blakserv/adminfn.c @@ -1405,10 +1405,10 @@ void AdminShowMemory(int session_id,admin_parm_type parms[], aprintf("%s\n",TimeStr(GetTime())); for (i=0;iallocated[i]); + aprintf("%-20s %9lu\n",GetMemoryStatName(i),mstat->allocated[i]); total += mstat->allocated[i]; } - aprintf("%-20s %4lu MB\n","-- Total",total/1024/1024); + aprintf("%-20s %6lu MB\n","-- Total",total/1024/1024); aprintf("-------------------------------------------\n"); } From 232a9bdd7e02f96ad02c166d08b29f00d3d9f3b8 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Thu, 29 Oct 2015 01:09:39 +0100 Subject: [PATCH 10/11] refactoring --- blakserv/astar.c | 153 ++++++++++++++++++++++++----------------------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/blakserv/astar.c b/blakserv/astar.c index 781c910f8a..7ebb7e58dd 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -24,25 +24,25 @@ astar AStar; #pragma region HEAP // refers to the i-th element on the heap -#define HEAP(i) (AStar.NodesData[i].heapslot) +#define HEAP(a, i) ((a)->NodesData[i].heapslot) -__inline bool AStarHeapCheck(room_type* Room, int i) +__inline bool AStarHeapCheck(astar* Astar, room_type* Room, int i) { int squares = Room->rowshighres * Room->colshighres; if (i >= squares) return true; - if (!HEAP(i)) + if (!HEAP(Astar, i)) return true; - float our = HEAP(i)->Data->combined; + float our = HEAP(Astar, i)->Data->combined; int lchild = LCHILD(i); int rchild = RCHILD(i); if (lchild < squares) { - astar_node* node = HEAP(lchild); + astar_node* node = HEAP(Astar, lchild); if (node) { if (node->Data->combined < our) @@ -51,7 +51,7 @@ __inline bool AStarHeapCheck(room_type* Room, int i) } if (rchild < squares) { - astar_node* node = HEAP(rchild); + astar_node* node = HEAP(Astar, rchild); if (node) { if (node->Data->combined < our) @@ -59,14 +59,14 @@ __inline bool AStarHeapCheck(room_type* Room, int i) } } - bool retval = AStarHeapCheck(Room, lchild); + bool retval = AStarHeapCheck(Astar, Room, lchild); if (!retval) return false; - return AStarHeapCheck(Room, rchild); + return AStarHeapCheck(Astar, Room, rchild); } -__inline void AStarWriteHeapToFile(room_type* Room) +__inline void AStarWriteHeapToFile(astar* Astar, room_type* Room) { int rows = Room->rowshighres; int cols = Room->colshighres; @@ -87,7 +87,7 @@ __inline void AStarWriteHeapToFile(room_type* Room) for (int k = 0; k < rangesize; k++) { - astar_node* node = AStar.NodesData[rangestart + k].heapslot; + astar_node* node = Astar->NodesData[rangestart + k].heapslot; if (node) sprintf(rowstring, "%s|%6.2f|", rowstring, node->Data->combined); @@ -107,103 +107,103 @@ __inline void AStarWriteHeapToFile(room_type* Room) FreeMemory(MALLOC_ID_ROOM, rowstring, 60000); } -__inline void AStarHeapSwap(int idx1, int idx2) +__inline void AStarHeapSwap(astar* Astar, int idx1, int idx2) { - astar_node* node1 = HEAP(idx1); - astar_node* node2 = HEAP(idx2); - HEAP(idx1) = node2; - HEAP(idx2) = node1; + astar_node* node1 = HEAP(Astar, idx1); + astar_node* node2 = HEAP(Astar, idx2); + HEAP(Astar, idx1) = node2; + HEAP(Astar, idx2) = node1; node1->Data->heapindex = idx2; node2->Data->heapindex = idx1; } -__inline void AStarHeapMoveUp(int Index) +__inline void AStarHeapMoveUp(astar* Astar, int Index) { int i = Index; - while (i > 0 && HEAP(i)->Data->combined < HEAP(PARENT(i))->Data->combined) + while (i > 0 && HEAP(Astar, i)->Data->combined < HEAP(Astar, PARENT(i))->Data->combined) { - AStarHeapSwap(i, PARENT(i)); + AStarHeapSwap(Astar, i, PARENT(i)); i = PARENT(i); } } -__inline void AStarHeapHeapify(int Index) +__inline void AStarHeapHeapify(astar* Astar, int Index) { int i = Index; do { int min = i; - if (HEAP(LCHILD(i)) && HEAP(LCHILD(i))->Data->combined < HEAP(min)->Data->combined) + if (HEAP(Astar, LCHILD(i)) && HEAP(Astar, LCHILD(i))->Data->combined < HEAP(Astar, min)->Data->combined) min = LCHILD(i); - if (HEAP(RCHILD(i)) && HEAP(RCHILD(i))->Data->combined < HEAP(min)->Data->combined) + if (HEAP(Astar, RCHILD(i)) && HEAP(Astar, RCHILD(i))->Data->combined < HEAP(Astar, min)->Data->combined) min = RCHILD(i); if (min == i) break; - AStarHeapSwap(i, min); + AStarHeapSwap(Astar, i, min); i = min; } while (true); } -__inline void AStarHeapInsert(astar_node* Node) +__inline void AStarHeapInsert(astar* Astar, astar_node* Node) { // save index of this node - Node->Data->heapindex = AStar.HeapSize; + Node->Data->heapindex = Astar->HeapSize; // add node at the end - HEAP(Node->Data->heapindex) = Node; + HEAP(Astar, Node->Data->heapindex) = Node; // increment - AStar.HeapSize++; + Astar->HeapSize++; // push node up until in place - AStarHeapMoveUp(Node->Data->heapindex); + AStarHeapMoveUp(Astar, Node->Data->heapindex); } -__inline void AStarHeapRemoveFirst() +__inline void AStarHeapRemoveFirst(astar* Astar) { - int lastIdx = AStar.HeapSize - 1; + int lastIdx = Astar->HeapSize - 1; // no elements if (lastIdx < 0) return; // decrement size - AStar.HeapSize--; + Astar->HeapSize--; // more than 1 if (lastIdx > 0) { // put last at root - AStarHeapSwap(0, lastIdx); + AStarHeapSwap(Astar, 0, lastIdx); // zero out the previous root at swapped slot - HEAP(lastIdx)->Data->heapindex = 0; - HEAP(lastIdx) = NULL; + HEAP(Astar, lastIdx)->Data->heapindex = 0; + HEAP(Astar, lastIdx) = NULL; // reorder tree - AStarHeapHeapify(0); + AStarHeapHeapify(Astar, 0); } // only one, clear head else { - HEAP(0)->Data->heapindex = 0; - HEAP(0) = NULL; + HEAP(Astar, 0)->Data->heapindex = 0; + HEAP(Astar, 0) = NULL; } } #pragma endregion #pragma region ASTAR -__inline void AStarAddBlockers() +__inline void AStarAddBlockers(astar* Astar) { Blocker* b; - b = AStar.Room->Blocker; + b = Astar->Room->Blocker; while (b) { // Don't add self. - if (b->ObjectID == AStar.ObjectID) + if (b->ObjectID == Astar->ObjectID) { b = b->Next; continue; @@ -214,8 +214,8 @@ __inline void AStarAddBlockers() int col = (int)roundf(b->Position.X / 256.0f); // Don't add blockers at the target coords. - if (abs(row - AStar.EndNode->Meta->Row) < DESTBLOCKIGNORE && - abs(col - AStar.EndNode->Meta->Col) < DESTBLOCKIGNORE) + if (abs(row - Astar->EndNode->Meta->Row) < DESTBLOCKIGNORE && + abs(col - Astar->EndNode->Meta->Col) < DESTBLOCKIGNORE) { b = b->Next; continue; @@ -231,10 +231,10 @@ __inline void AStarAddBlockers() int c = col + coloffset; // outside - if (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) + if (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) continue; - astar_node* node = &AStar.Room->Grid[r][c]; + astar_node* node = &Astar->Room->Grid[r][c]; node->Data->isBlocked = true; } } @@ -242,7 +242,7 @@ __inline void AStarAddBlockers() } } -__inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) +__inline bool AStarCanWalkEdge(astar* Astar, astar_node* Node, astar_node* Neighbour, unsigned short KnowsVal, unsigned short CanVal) { Wall* blockWall; @@ -254,7 +254,7 @@ __inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned if (!knows) { // bsp query - can = BSPCanMoveInRoom(AStar.Room, &Node->Meta->Location, &Neighbour->Meta->Location, AStar.ObjectID, false, &blockWall, true); + can = BSPCanMoveInRoom(Astar->Room, &Node->Meta->Location, &Neighbour->Meta->Location, Astar->ObjectID, false, &blockWall, true); // save to cache *Node->Edges |= (can) ? CanVal : KnowsVal; @@ -267,26 +267,26 @@ __inline bool AStarCanWalkEdge(astar_node* Node, astar_node* Neighbour, unsigned return can; #else - return BSPCanMoveInRoom(AStar.Room, &Node->Meta->Location, &Neighbour->Meta->Location, AStar.ObjectID, false, &blockWall, true); + return BSPCanMoveInRoom(Astar->Room, &Node->Meta->Location, &Neighbour->Meta->Location, Astar->ObjectID, false, &blockWall, true); #endif } -__inline void AStarProcessNeighbour(astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) +__inline void AStarProcessNeighbour(astar* Astar, astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) { // skip any neighbour outside of grid or sector, already processed or blocked if (!Neighbour || !Neighbour->Leaf || Neighbour->Data->isInClosedList || Neighbour->Data->isBlocked) return; // can't walk edge from node to neighbour - if (!AStarCanWalkEdge(Node, Neighbour, KnowsVal, CanVal)) + if (!AStarCanWalkEdge(Astar, Node, Neighbour, KnowsVal, CanVal)) return; // heuristic (~ estimated distance from node to end) // need to do this only once if (Neighbour->Data->heuristic == 0.0f) { - float dx = (float)abs(Neighbour->Meta->Col - AStar.EndNode->Meta->Col); - float dy = (float)abs(Neighbour->Meta->Row - AStar.EndNode->Meta->Row); + float dx = (float)abs(Neighbour->Meta->Col - Astar->EndNode->Meta->Col); + float dy = (float)abs(Neighbour->Meta->Row - Astar->EndNode->Meta->Row); // octile-distance Neighbour->Data->heuristic = @@ -306,7 +306,7 @@ __inline void AStarProcessNeighbour(astar_node* Node, astar_node* Neighbour, flo Neighbour->Data->combined = Neighbour->Data->cost + Neighbour->Data->heuristic; // add it sorted to the open list - AStarHeapInsert(Neighbour); + AStarHeapInsert(Astar, Neighbour); } // CASE 2) @@ -327,16 +327,16 @@ __inline void AStarProcessNeighbour(astar_node* Node, astar_node* Neighbour, flo // reorder it upwards in the heap tree, don't care about downordering // since costs are lower and heuristic is always the same, // it's guaranteed to be moved up - AStarHeapMoveUp(Neighbour->Data->heapindex); + AStarHeapMoveUp(Astar, Neighbour->Data->heapindex); } } } -__inline bool AStarProcessFirst() +__inline bool AStarProcessFirst(astar* Astar) { // shortcuts - astar_node* node = HEAP(0); - astar_node* endNode = AStar.EndNode; + astar_node* node = HEAP(Astar, 0); + astar_node* endNode = Astar->EndNode; // openlist empty, unreachable if (!node) @@ -346,7 +346,7 @@ __inline bool AStarProcessFirst() if (abs(node->Meta->Col - endNode->Meta->Col) < CLOSEENOUGHDIST && abs(node->Meta->Row - endNode->Meta->Row) < CLOSEENOUGHDIST) { - AStar.LastNode = node; + Astar->LastNode = node; return false; } @@ -360,52 +360,52 @@ __inline bool AStarProcessFirst() // n r = node->Meta->Row - 1; c = node->Meta->Col + 0; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); // e r = node->Meta->Row + 0; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); // s r = node->Meta->Row + 1; c = node->Meta->Col + 0; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); // w r = node->Meta->Row + 0; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); // diagonal neighbours // ne r = node->Meta->Row - 1; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); // se r = node->Meta->Row + 1; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); // sw r = node->Meta->Row + 1; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); // nw r = node->Meta->Row - 1; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= AStar.Room->rowshighres || c >= AStar.Room->colshighres) ? NULL : &AStar.Room->Grid[r][c]; - AStarProcessNeighbour(node, n, COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); + n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); /****************************************************************/ @@ -413,7 +413,7 @@ __inline bool AStarProcessFirst() node->Data->isInClosedList = true; // remove it from the open list - AStarHeapRemoveFirst(); + AStarHeapRemoveFirst(Astar); return true; } @@ -493,6 +493,7 @@ void AStarGenerateGrid(room_type* Room) astar_node* node = &Room->Grid[i][j]; // setup reference to data (costs etc.) in erasable mem area + // todo: change away from AStar to param node->Data = &AStar.NodesData[idx1]; node->Meta = &AStar.Grid[row][col]; @@ -641,14 +642,14 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // mark nodes blocked by objects - AStarAddBlockers(); + AStarAddBlockers(&AStar); // insert startnode into heap-tree - AStarHeapInsert(AStar.StartNode); + AStarHeapInsert(&AStar, AStar.StartNode); // the algorithm finishes if we either hit a node close enough to endnode // or if there is no more entries in the open list (unreachable) - while (AStarProcessFirst()) {} + while (AStarProcessFirst(&AStar)) {} //AStarWriteGridToFile(Room); //AStarWriteHeapToFile(Room); From 2581b49333332c1542ab7e758163bb9936889db2 Mon Sep 17 00:00:00 2001 From: cyberjunk Date: Fri, 30 Oct 2015 20:21:00 +0100 Subject: [PATCH 11/11] Multithreaded Astar --- blakcomp/function.c | 7 + blakserv/astar.c | 799 ++++++++++---- blakserv/astar.h | 245 +++-- blakserv/astar_classes.h | 359 +++++++ blakserv/blakserv.h | 12 +- blakserv/blakserv.vcxproj | 2 + blakserv/blakserv.vcxproj.filters | 6 + blakserv/ccode.c | 49 +- blakserv/garbage.c | 11 + blakserv/memory.c | 10 + blakserv/queue.h | 78 ++ blakserv/roofile.c | 993 +++++++++++------- blakserv/roofile.h | 46 +- blakserv/roomdata.c | 16 +- blakserv/timer.c | 7 +- kod/include/blakston.khd | 23 +- .../holder/nomoveon/battler/monster.kod | 171 ++- kod/object/passive/brain.kod | 6 + kod/util/system.kod | 2 + 19 files changed, 2090 insertions(+), 752 deletions(-) create mode 100644 blakserv/astar_classes.h create mode 100644 blakserv/queue.h diff --git a/blakcomp/function.c b/blakcomp/function.c index caf4b0f150..56b5776631 100644 --- a/blakcomp/function.c +++ b/blakcomp/function.c @@ -178,6 +178,13 @@ id_struct BuiltinIds[] = { {"realtime", I_MISSING, 36, 0, I_CLASS}, {"gameeventengine",I_MISSING, 37, 0, I_CLASS}, {"escapedconvict",I_MISSING, 38, 0, I_CLASS}, +{"MoveCallback", I_MISSING, 39, 0, I_MESSAGE }, +{"iRow", I_MISSING, 40, 0, I_PARAMETER }, +{"iCol", I_MISSING, 41, 0, I_PARAMETER }, +{"iFineRow", I_MISSING, 42, 0, I_PARAMETER }, +{"iFineCol", I_MISSING, 43, 0, I_PARAMETER }, +{"iType", I_MISSING, 44, 0, I_PARAMETER }, +{"iFlags", I_MISSING, 45, 0, I_PARAMETER }, }; int numbuiltins = (sizeof(BuiltinIds)/sizeof(id_struct)); diff --git a/blakserv/astar.c b/blakserv/astar.c index 7ebb7e58dd..e1baf753de 100644 --- a/blakserv/astar.c +++ b/blakserv/astar.c @@ -20,7 +20,15 @@ Improvements over the old implementation: #include "blakserv.h" -astar AStar; +#define LCHILD(x) (2 * x + 1) +#define RCHILD(x) (2 * x + 2) +#define PARENT(x) ((x-1) / 2) + +astar AStar[THREADSCOUNT]; +astar_query_queue AStarQueries = astar_query_queue(QUERYQUEUESIZE); +astar_response_queue AStarResponses = astar_response_queue(RESPONSEQUEUESIZE); + +void AStarThreadProc(astar* Astar); #pragma region HEAP // refers to the i-th element on the heap @@ -70,7 +78,7 @@ __inline void AStarWriteHeapToFile(astar* Astar, room_type* Room) { int rows = Room->rowshighres; int cols = Room->colshighres; - char* rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 60000); + char* rowstring = (char *)AStarAlloc(Astar, 60000); FILE *fp = fopen("heapdebug.txt", "a"); if (fp) { @@ -104,7 +112,7 @@ __inline void AStarWriteHeapToFile(astar* Astar, room_type* Room) sprintf(rowstring, "%s\n", rowstring); fclose(fp); } - FreeMemory(MALLOC_ID_ROOM, rowstring, 60000); + AStarFree(Astar, rowstring, 60000); } __inline void AStarHeapSwap(astar* Astar, int idx1, int idx2) @@ -199,7 +207,7 @@ __inline void AStarAddBlockers(astar* Astar) { Blocker* b; - b = Astar->Room->Blocker; + b = Astar->Room->Room->Blocker; while (b) { // Don't add self. @@ -231,7 +239,7 @@ __inline void AStarAddBlockers(astar* Astar) int c = col + coloffset; // outside - if (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) + if (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) continue; astar_node* node = &Astar->Room->Grid[r][c]; @@ -246,7 +254,6 @@ __inline bool AStarCanWalkEdge(astar* Astar, astar_node* Node, astar_node* Neigh { Wall* blockWall; -#if EDGESCACHEENABLED bool knows = (((*Node->Edges) & KnowsVal) == KnowsVal); bool can; @@ -254,7 +261,7 @@ __inline bool AStarCanWalkEdge(astar* Astar, astar_node* Node, astar_node* Neigh if (!knows) { // bsp query - can = BSPCanMoveInRoom(Astar->Room, &Node->Meta->Location, &Neighbour->Meta->Location, Astar->ObjectID, false, &blockWall, true); + can = BSPCanMoveInRoom(Astar->Room->Room, &Node->Meta->Location, &Neighbour->Meta->Location, Astar->ObjectID, false, &blockWall, true); // save to cache *Node->Edges |= (can) ? CanVal : KnowsVal; @@ -265,10 +272,6 @@ __inline bool AStarCanWalkEdge(astar* Astar, astar_node* Node, astar_node* Neigh can = (((*Node->Edges) & CanVal) == CanVal); return can; - -#else - return BSPCanMoveInRoom(Astar->Room, &Node->Meta->Location, &Neighbour->Meta->Location, Astar->ObjectID, false, &blockWall, true); -#endif } __inline void AStarProcessNeighbour(astar* Astar, astar_node* Node, astar_node* Neighbour, float StepCost, unsigned short KnowsVal, unsigned short CanVal) @@ -332,7 +335,7 @@ __inline void AStarProcessNeighbour(astar* Astar, astar_node* Node, astar_node* } } -__inline bool AStarProcessFirst(astar* Astar) +__inline bool AStarProcessFirstOnHeap(astar* Astar) { // shortcuts astar_node* node = HEAP(Astar, 0); @@ -360,25 +363,25 @@ __inline bool AStarProcessFirst(astar* Astar) // n r = node->Meta->Row - 1; c = node->Meta->Col + 0; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_N, EDGECACHE_CAN_N); // e r = node->Meta->Row + 0; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_E, EDGECACHE_CAN_E); // s r = node->Meta->Row + 1; c = node->Meta->Col + 0; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_S, EDGECACHE_CAN_S); // w r = node->Meta->Row + 0; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST, EDGECACHE_KNOWS_W, EDGECACHE_CAN_W); // diagonal neighbours @@ -386,25 +389,25 @@ __inline bool AStarProcessFirst(astar* Astar) // ne r = node->Meta->Row - 1; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_NE, EDGECACHE_CAN_NE); // se r = node->Meta->Row + 1; c = node->Meta->Col + 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_SE, EDGECACHE_CAN_SE); // sw r = node->Meta->Row + 1; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_SW, EDGECACHE_CAN_SW); // nw r = node->Meta->Row - 1; c = node->Meta->Col - 1; - n = (r < 0 || c < 0 || r >= Astar->Room->rowshighres || c >= Astar->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; + n = (r < 0 || c < 0 || r >= Astar->Room->Room->rowshighres || c >= Astar->Room->Room->colshighres) ? NULL : &Astar->Room->Grid[r][c]; AStarProcessNeighbour(Astar, node, n, COST_DIAG, EDGECACHE_KNOWS_NW, EDGECACHE_CAN_NW); /****************************************************************/ @@ -418,12 +421,12 @@ __inline bool AStarProcessFirst(astar* Astar) return true; } -void AStarWriteGridToFile(room_type* Room) +void AStarWriteGridToFile(astar_room* Room) { - int rows = Room->rowshighres; - int cols = Room->colshighres; + int rows = Room->Room->rowshighres; + int cols = Room->Room->colshighres; - char *rowstring = (char *)AllocateMemory(MALLOC_ID_ROOM, 50000); + char *rowstring = (char *)malloc(50000); FILE *fp = fopen("griddebug.txt", "w"); if (fp) @@ -441,215 +444,334 @@ void AStarWriteGridToFile(room_type* Room) fclose(fp); } - FreeMemory(MALLOC_ID_ROOM, rowstring, 50000); + free(rowstring); +} + +void AStarInit() +{ + // start workers + for (int i = 0; i < THREADSCOUNT; i++) + { + AStar[i].Commands = new astar_command_queue(COMMANDQUEUESIZE); + AStar[i].Thread = new ::std::thread(AStarThreadProc, &AStar[i]); + } +} + +void AStarShutdown() +{ + // stop workers + for (int i = 0; i < THREADSCOUNT; i++) + AStar[i].IsRunning.store(false); } +#pragma endregion -void AStarGenerateGrid(room_type* Room) +#pragma region THREADING +void AStarHandleLoadRoom(astar* Astar, astar_command_loadroom* Command) { - if (Room->colshighres > MAXGRIDCOLS || Room->rowshighres > MAXGRIDROWS) + room_type* bsproom = (room_type*)AStarAlloc(Astar, sizeof(room_type)); + + // failed to load room + if (!BSPLoadRoom(Command->file, bsproom, Astar)) + { + AStarFree(Astar, bsproom, sizeof(room_type)); + return; + } + + // set id on our own instance + bsproom->roomdata_id = Command->id; + bsproom->resource_id = 0; + + // create astar room + astar_room* room = (astar_room*)AStarAlloc(Astar, sizeof(astar_room)); + room->Room = bsproom; + + if (bsproom->colshighres > MAXGRIDCOLS || bsproom->rowshighres > MAXGRIDROWS) eprintf("ALERT!! You tried to load a room with a grid-size beyond allowed limits! AStar will fail."); /**********************************************************************/ -#if EDGESCACHEENABLED // allocate memory to cache edges (BSP queries) - Room->EdgesCacheSize = Room->colshighres * Room->rowshighres * sizeof(unsigned short); - - Room->EdgesCache = (unsigned short*)AllocateMemory( - MALLOC_ID_ASTAR, Room->EdgesCacheSize); + room->EdgesCacheSize = bsproom->colshighres * bsproom->rowshighres * sizeof(unsigned short); + room->EdgesCache = (unsigned short*)AStarAlloc(Astar, room->EdgesCacheSize); + ZeroMemory(room->EdgesCache, room->EdgesCacheSize); - AStarClearEdgesCache(Room); -#endif - /**********************************************************************/ -#if PATHCACHEENABLED - // setup path cache - for (int i = 0; i < PATHCACHESIZE; i++) - Room->Paths[i] = new astar_path(); -#endif /**********************************************************************/ // allocate memory for the persistent nodesinfo (typical 2d array by **) - Room->Grid = (astar_node**)AllocateMemory( - MALLOC_ID_ASTAR, Room->rowshighres * sizeof(astar_node*)); + room->Grid = (astar_node**)AStarAlloc(Astar, bsproom->rowshighres * sizeof(astar_node*)); // allocate rows - for (int i = 0; i < Room->rowshighres; i++) - Room->Grid[i] = (astar_node*)AllocateMemory( - MALLOC_ID_ASTAR, Room->colshighres * sizeof(astar_node)); + for (int i = 0; i < bsproom->rowshighres; i++) + room->Grid[i] = (astar_node*)AStarAlloc(Astar, bsproom->colshighres * sizeof(astar_node)); /**********************************************************************/ // setup all squares - for (int i = 0; i < Room->rowshighres; i++) + for (int i = 0; i < bsproom->rowshighres; i++) { - int row = (i < MAXGRIDROWS) ? i : 0; + int row = (i < MAXGRIDROWS) ? i : 0; - for (int j = 0; j < Room->colshighres; j++) - { - int col = (j < MAXGRIDCOLS) ? j : 0; - int idx1 = row*Room->colshighres + col; - int idx2 = i*Room->colshighres + j; + for (int j = 0; j < bsproom->colshighres; j++) + { + int col = (j < MAXGRIDCOLS) ? j : 0; + int idx1 = row*bsproom->colshighres + col; + int idx2 = i*bsproom->colshighres + j; - astar_node* node = &Room->Grid[i][j]; + astar_node* node = &room->Grid[i][j]; // setup reference to data (costs etc.) in erasable mem area - // todo: change away from AStar to param - node->Data = &AStar.NodesData[idx1]; - node->Meta = &AStar.Grid[row][col]; + node->Data = &Astar->NodesData[idx1]; + node->Meta = &Astar->Grid[row][col]; -#if EDGESCACHEENABLED // setup reference to edgesdata in edgescache - node->Edges = &Room->EdgesCache[idx2]; -#endif + node->Edges = &room->EdgesCache[idx2]; - float f1, f2, f3; + float f1, f2, f3; - // mark if this square is inside or outside of room - BSPGetHeight(Room, &node->Meta->Location, &f1, &f2, &f3, &node->Leaf); + // mark if this square is inside or outside of room + BSPGetHeight(bsproom, &node->Meta->Location, &f1, &f2, &f3, &node->Leaf); } } + + /**********************************************************************/ + + // add to map + Astar->Rooms->insert(::std::pair(Command->id, room)); } -void AStarFreeGrid(room_type* Room) +void AStarHandleUnloadRoom(astar* Astar, astar_command_unloadroom* Command) { -#if EDGESCACHEENABLED - // free edgescache mem - FreeMemory(MALLOC_ID_ASTAR, Room->EdgesCache, Room->EdgesCacheSize); -#endif + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->id); -#if PATHCACHEENABLED - // free pathes list allocations - for (int i = 0; i < PATHCACHESIZE; i++) - delete Room->Paths[i]; -#endif + // not found + if (item == Astar->Rooms->end()) + return; + + astar_room* astarroom = item->second; + room_type* bsproom = item->second->Room; // free each row mem - for (int i = 0; i < Room->rowshighres; i++) - FreeMemory(MALLOC_ID_ASTAR, Room->Grid[i], Room->colshighres * sizeof(astar_node)); + for (int i = 0; i < bsproom->rowshighres; i++) + AStarFree(Astar, astarroom->Grid[i], bsproom->colshighres * sizeof(astar_node)); // free rowsmem - FreeMemory(MALLOC_ID_ASTAR, Room->Grid, Room->rowshighres * sizeof(astar_node*)); + AStarFree(Astar, astarroom->Grid, bsproom->rowshighres * sizeof(astar_node*)); + + // free edgescache mem + AStarFree(Astar, astarroom->EdgesCache, astarroom->EdgesCacheSize); + + // free room + BSPFreeRoom(bsproom, Astar); + + // remove from map + Astar->Rooms->erase(item); } -#if EDGESCACHEENABLED -void AStarClearEdgesCache(room_type* Room) +void AStarHandleBlockerAdd(astar* Astar, astar_command_blockeradd* Command) { - ZeroMemory(Room->EdgesCache, Room->EdgesCacheSize); + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); + + // not found + if (item == Astar->Rooms->end()) + return; + + // call BSP function on our room instance + BSPBlockerAdd( + item->second->Room, + Command->objectid, + &Command->p); } -#endif -#if PATHCACHEENABLED -void AStarClearPathCache(room_type* Room) +void AStarHandleBlockerRemove(astar* Astar, astar_command_blockerremove* Command) { - for (int i = 0; i < PATHCACHESIZE; i++) - Room->Paths[i]->clear(); + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); + + // not found + if (item == Astar->Rooms->end()) + return; + + // call BSP function on our room instance + BSPBlockerRemove( + item->second->Room, + Command->objectid); } -bool AStarGetStepFromCache(room_type* Room, astar_node* S, astar_node* E, V2* P, unsigned int* Flags, int ObjectID) +void AStarHandleBlockerMove(astar* Astar, astar_command_blockermove* Command) { - Wall* blockWall; + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); - for (int i = 0; i < PATHCACHESIZE; i++) - { - astar_path* path = Room->Paths[i]; + // not found + if (item == Astar->Rooms->end()) + return; - // a valid path would have two entries - if (path->size() < 2) - continue; + // call BSP function on our room instance + BSPBlockerMove( + item->second->Room, + Command->objectid, + &Command->p); +} - // check start (better match exactly, or we might stepping back?) - astar_node* first = path->front(); - if (first->Meta->Row != S->Meta->Row || first->Meta->Col != S->Meta->Col) - continue; +void AStarHandleBlockerClear(astar* Astar, astar_command_blockerclear* Command) +{ + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); - // check end (has small tolerance) - astar_node* last = path->back(); - if (abs(last->Meta->Row - E->Meta->Row) > PATHCACHETOLERANCE || - abs(last->Meta->Col - E->Meta->Col) > PATHCACHETOLERANCE) - continue; + // not found + if (item == Astar->Rooms->end()) + return; - // match! now remove first, so we also match on next step - path->pop_front(); + // call BSP function on our room instance + BSPBlockerClear(item->second->Room); +} - // get the next step endpoint - astar_node* next = path->front(); +void AStarHandleMoveSector(astar* Astar, astar_command_movesector* Command) +{ + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); + + // not found + if (item == Astar->Rooms->end()) + return; - // make sure we can still move to this next endpoint (cares for moved objects!) - // note: if objects block a cached path, the path will still be walked until the block occurs! - // to improve this revalidate the whole path from the first to the last node here - if (!BSPCanMoveInRoom(Room, &first->Meta->Location, &next->Meta->Location, ObjectID, false, &blockWall, false)) - continue; + // call BSP function on our room instance + BSPMoveSector( + item->second->Room, + Command->serverid, + Command->floor, + Command->height, + Command->speed); - // for diagonal moves mark to be long step (required for timer elapse) - if (abs(first->Meta->Col - next->Meta->Col) && - abs(first->Meta->Row - next->Meta->Row)) - { - *Flags |= ESTATE_LONG_STEP; - } - else - *Flags &= ~ESTATE_LONG_STEP; + // invalidate edges cache + ZeroMemory(item->second->EdgesCache, item->second->EdgesCacheSize); - // set step endpoint - *P = next->Meta->Location; + // path cache is only in main-instances +} - // cache hit - return true; +void AStarHandleChangeTexture(astar* Astar, astar_command_changetexture* Command) +{ + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Command->roomid); + + // not found + if (item == Astar->Rooms->end()) + return; + + // call BSP function on our room instance + BSPChangeTexture( + item->second->Room, + Command->serverid, + Command->newtexture, + Command->flags); + + // invalidate edges cache + ZeroMemory(item->second->EdgesCache, item->second->EdgesCacheSize); + + // path cache is only in main-instances +} + +void AStarHandleCommand(astar* Astar, astar_command* Command) +{ + switch (Command->commandType()) + { + case astar_command_type::AStarLoadRoom: + AStarHandleLoadRoom(Astar, (astar_command_loadroom*)Command); + break; + case astar_command_type::AStarUnloadRoom: + AStarHandleUnloadRoom(Astar, (astar_command_unloadroom*)Command); + break; + case astar_command_type::AStarBlockerAdd: + AStarHandleBlockerAdd(Astar, (astar_command_blockeradd*)Command); + break; + case astar_command_type::AStarBlockerRemove: + AStarHandleBlockerRemove(Astar, (astar_command_blockerremove*)Command); + break; + case astar_command_type::AStarBlockerMove: + AStarHandleBlockerMove(Astar, (astar_command_blockermove*)Command); + break; + case astar_command_type::AStarBlockerClear: + AStarHandleBlockerClear(Astar, (astar_command_blockerclear*)Command); + break; + case astar_command_type::AStarMoveSector: + AStarHandleMoveSector(Astar, (astar_command_movesector*)Command); + break; + case astar_command_type::AStarChangeTexture: + AStarHandleChangeTexture(Astar, (astar_command_changetexture*)Command); + break; } - - // no cache hit - return false; + delete Command; } -#endif -bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +astar_pathresponse* AStarRunPathQuery(astar* Astar, astar_pathquery* Query) { + // return value + astar_pathresponse* response = + new astar_pathresponse(Query->roomid, Query->objectid, Query->s, Query->e, Query->flags); + + // lookup room + ::std::map::iterator item = + Astar->Rooms->find(Query->roomid); + + // not found + if (item == Astar->Rooms->end()) + return response; + + astar_room* room = item->second; + room_type* bsproom = room->Room; + + /**********************************************************************/ + // convert coordinates from ROO floatingpoint to // highres scale in integers - int startrow = (int)roundf(S->Y / 256.0f); - int startcol = (int)roundf(S->X / 256.0f); - int endrow = (int)roundf(E->Y / 256.0f); - int endcol = (int)roundf(E->X / 256.0f); + int startrow = (int)roundf(Query->s.Y / 256.0f); + int startcol = (int)roundf(Query->s.X / 256.0f); + int endrow = (int)roundf(Query->e.Y / 256.0f); + int endcol = (int)roundf(Query->e.X / 256.0f); // all 4 values must be within grid array bounds! - if (startrow < 0 || startrow >= Room->rowshighres || - startcol < 0 || startcol >= Room->colshighres || - endrow < 0 || endrow >= Room->rowshighres || - endcol < 0 || endcol >= Room->colshighres) - return false; + if (startrow < 0 || startrow >= bsproom->rowshighres || + startcol < 0 || startcol >= bsproom->colshighres || + endrow < 0 || endrow >= bsproom->rowshighres || + endcol < 0 || endcol >= bsproom->colshighres) + return response; /**********************************************************************/ // set data on astar - AStar.StartNode = &Room->Grid[startrow][startcol]; - AStar.EndNode = &Room->Grid[endrow][endcol]; - AStar.Room = Room; - AStar.LastNode = NULL; - AStar.ObjectID = ObjectID; - AStar.HeapSize = 0; + Astar->StartNode = &room->Grid[startrow][startcol]; + Astar->EndNode = &room->Grid[endrow][endcol]; + Astar->Room = room; + Astar->LastNode = NULL; + Astar->ObjectID = Query->objectid; + Astar->HeapSize = 0; /**********************************************************************/ -#if PATHCACHEENABLED - // first try path cache lookup - if (AStarGetStepFromCache(Room, AStar.StartNode, AStar.EndNode, P, Flags, ObjectID)) - return true; -#endif - /**********************************************************************/ // prepare non-persistent astar grid data memory - ZeroMemory(AStar.NodesData, AStar.NodesDataSize); - //ZeroMemory(AStar.NodesData, Room->rowshighres * Room->colshighres * sizeof(astar_node_data)); + ZeroMemory(Astar->NodesData, Astar->NodesDataSize); + //ZeroMemory(Astar->NodesData, bsproom->rowshighres * bsproom->colshighres * sizeof(astar_node_data)); /**********************************************************************/ // mark nodes blocked by objects - AStarAddBlockers(&AStar); + AStarAddBlockers(Astar); // insert startnode into heap-tree - AStarHeapInsert(&AStar, AStar.StartNode); + AStarHeapInsert(Astar, Astar->StartNode); // the algorithm finishes if we either hit a node close enough to endnode // or if there is no more entries in the open list (unreachable) - while (AStarProcessFirst(&AStar)) {} + while (AStarProcessFirstOnHeap(Astar)) {} //AStarWriteGridToFile(Room); //AStarWriteHeapToFile(Room); @@ -657,105 +779,342 @@ bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Fla /**********************************************************************/ // unreachable - if (!AStar.LastNode) - return false; - -#if PATHCACHEENABLED - // check for resetting nextpathidx - if (Room->NextPathIdx >= PATHCACHESIZE) - Room->NextPathIdx = 0; - - // get path from cache and clear it - astar_path* path = Room->Paths[Room->NextPathIdx]; - Room->NextPathIdx++; - - // clear old cached path - path->clear(); -#else - // use a temporary ::std:list - astar_path _path; - astar_path* path = &_path; -#endif + if (!Astar->LastNode) + return response; // walk back parent pointers from lastnode (=path) - astar_node* node = AStar.LastNode; + astar_node* node = Astar->LastNode; while (node) { - path->push_front(node); - node = node->Data->parent; - } + // create path node (use malloc, "not our mem", will be used and freed in mainthread) + astar_path_node* pathnode = (astar_path_node*)malloc(sizeof(astar_path_node)); - // not interested in first one (=startnode) - path->pop_front(); + // set values + pathnode->Row = node->Meta->Row; + pathnode->Col = node->Meta->Col; + pathnode->Location = node->Meta->Location; - // this is the next one - node = path->front(); + // add to path + response->path.push_front(pathnode); - // for diagonal moves mark to be long step (required for timer elapse) - if (abs(node->Meta->Col - AStar.StartNode->Meta->Col) && - abs(node->Meta->Row - AStar.StartNode->Meta->Row)) - { - *Flags |= ESTATE_LONG_STEP; + // process next node + node = node->Data->parent; } - else - *Flags &= ~ESTATE_LONG_STEP; - // set step endpoint - *P = node->Meta->Location; + return response; +} - return true; +astar_response* AStarRunQuery(astar* Astar, astar_query* Query) +{ + astar_response* ret = NULL; + switch (Query->queryType()) + { + case astar_query_type::AStarPathQuery: + ret = AStarRunPathQuery(Astar, (astar_pathquery*)Query); + break; + } + return ret; } -void AStarInit() +void AStarThreadProc(astar* Astar) { + Astar->IsRunning = true; + Astar->IsPaused = false; + Astar->DoPause = false; + Astar->Memory = sizeof(astar); + + /**************************************************************************************/ + /* STARTUP */ + /**************************************************************************************/ + // memory for astar data erased with each query - AStar.NodesDataSize = NODESDATASQUARES * sizeof(astar_node_data); - AStar.NodesData = (astar_node_data*)AllocateMemory( - MALLOC_ID_ASTAR, AStar.NodesDataSize); + Astar->NodesDataSize = NODESDATASQUARES * sizeof(astar_node_data); + Astar->NodesData = (astar_node_data*)AStarAlloc(Astar, Astar->NodesDataSize); + + // basic init values + Astar->StartNode = NULL; + Astar->EndNode = NULL; + Astar->LastNode = NULL; + Astar->ObjectID = 0; + Astar->HeapSize = 0; + Astar->Room = NULL; - AStar.StartNode = NULL; - AStar.EndNode = NULL; - AStar.LastNode = NULL; - AStar.ObjectID = 0; - AStar.HeapSize = 0; - AStar.Room = NULL; + // create rooms list + Astar->Rooms = new astar_rooms(); // allocate memory for the persistent grid - AStar.Grid = (astar_node_meta**)AllocateMemory(MALLOC_ID_ASTAR, MAXGRIDROWS * sizeof(astar_node_meta*)); + Astar->Grid = (astar_node_meta**)AStarAlloc(Astar, MAXGRIDROWS * sizeof(astar_node_meta*)); // allocate rows for (int i = 0; i < MAXGRIDROWS; i++) - AStar.Grid[i] = (astar_node_meta*)AllocateMemory(MALLOC_ID_ASTAR, MAXGRIDCOLS * sizeof(astar_node_meta)); + Astar->Grid[i] = (astar_node_meta*)AStarAlloc(Astar, MAXGRIDCOLS * sizeof(astar_node_meta)); // setup all squares for (int i = 0; i < MAXGRIDROWS; i++) { - for (int j = 0; j < MAXGRIDCOLS; j++) - { - int idx = i*MAXGRIDCOLS + j; + for (int j = 0; j < MAXGRIDCOLS; j++) + { + int idx = i*MAXGRIDCOLS + j; - astar_node_meta* node = &AStar.Grid[i][j]; + astar_node_meta* node = &Astar->Grid[i][j]; - // set row/col - node->Row = i; - node->Col = j; + // set row/col + node->Row = i; + node->Col = j; - // floatingpoint coordinates of the center of the square in ROO fineness (for queries) - node->Location.X = j * 256.0f;// +128.0f; - node->Location.Y = i * 256.0f;// +128.0f; - } + // floatingpoint coordinates of the center of the square in ROO fineness (for queries) + node->Location.X = j * 256.0f;// +128.0f; + node->Location.Y = i * 256.0f;// +128.0f; + } } -} -void AStarShutdown() -{ - FreeMemory(MALLOC_ID_ASTAR, AStar.NodesData, AStar.NodesDataSize); - + /**************************************************************************************/ + /* WORKING */ + /**************************************************************************************/ + + while (Astar->IsRunning.load()) + { + // paused mode + if (Astar->DoPause) + { + // sleep if pause is active + if (Astar->IsPaused) + ::std::this_thread::sleep_for(::std::chrono::milliseconds(WORKERSLEEPTIMEMS)); + + // go to pause-mode + else + { + // handle all remaining commands + while (astar_command* cmd = Astar->Commands->dequeue()) + AStarHandleCommand(Astar, cmd); + + Astar->IsPaused = true; + } + } + + // normal mode + else + { + // make sure pause flag is unset + Astar->IsPaused = false; + + // handle all pending commands + while (astar_command* cmd = Astar->Commands->dequeue()) + AStarHandleCommand(Astar, cmd); + + // try to grab the next query-task + astar_query* query = AStarQueries.dequeue(); + + // if we got one, process it and enqueue the possible response + if (query) + { + astar_response* response = AStarRunQuery(Astar, query); + + if (response) + AStarResponses.enqueue(response); + + delete query; + + // let mainthread know about a new path being ready + PostThreadMessage(main_thread_id, WM_BLAK_MAIN_PATH_READY, 0, 0); + } + + // if there was no query to process, sleep a bit + else + ::std::this_thread::sleep_for(::std::chrono::milliseconds(WORKERSLEEPTIMEMS)); + } + } + + /**************************************************************************************/ + /* SHUTDOWN */ + /**************************************************************************************/ + + AStarFree(Astar, Astar->NodesData, Astar->NodesDataSize); + // free each meta grid row for (int i = 0; i < MAXGRIDROWS; i++) - FreeMemory(MALLOC_ID_ASTAR, AStar.Grid[i], MAXGRIDCOLS * sizeof(astar_node_meta)); + AStarFree(Astar, Astar->Grid[i], MAXGRIDCOLS * sizeof(astar_node_meta)); // free meta grid - FreeMemory(MALLOC_ID_ASTAR, AStar.Grid, MAXGRIDROWS * sizeof(astar_node_meta*)); + AStarFree(Astar, Astar->Grid, MAXGRIDROWS * sizeof(astar_node_meta*)); + + // delete rooms (todo: need cleanup of items, but blakserv is likely shutting down..) + delete Astar->Rooms; } -#pragma endregion + +void AStarDeliverPathResponse(astar_pathresponse* Response) +{ + room_node* roomnode = GetRoomDataByID(Response->roomid); + V2 p = { 0.0f, 0.0f }; + Wall* blockWall = NULL; + + if (!roomnode) + return; + + // unreachable + if (Response->path.size() == 0) + { + /*******************************************************************/ + // Add to nopath cache + /*******************************************************************/ + // check for resetting nextpathidx + if (roomnode->data.NextNoPathIdx >= NOPATHCACHESIZE) + roomnode->data.NextNoPathIdx = 0; + + // get nopath instance from cache + astar_nopath* nopath = roomnode->data.NoPaths[roomnode->data.NextNoPathIdx]; + roomnode->data.NextNoPathIdx++; + + // update values + nopath->S = Response->s; + nopath->E = Response->e; + nopath->Tick = ::std::chrono::high_resolution_clock::now(); + + // use heuristic + if (BSPGetStepFromHeuristic(&roomnode->data, &Response->s, &Response->e, &p, &Response->flags, Response->objectid)) + BSPInvokeMoveCallback(Response->objectid, MC_HEURISTIC, Response->flags, &p); + + // fully blocked + else + BSPInvokeMoveCallback(Response->objectid, MC_UNREACHABLE, Response->flags, &p); + } + else + { + /*******************************************************************/ + // Add to path cache + /*******************************************************************/ + + // check for resetting nextpathidx + if (roomnode->data.NextPathIdx >= PATHCACHESIZE) + roomnode->data.NextPathIdx = 0; + + // get path from cache and clear it + astar_path* path = roomnode->data.Paths[roomnode->data.NextPathIdx]; + roomnode->data.NextPathIdx++; + + // clear old cached path, use instances in response in our path + BSPClearPath(path); + path->assign(Response->path.begin(), Response->path.end()); + + /*******************************************************************/ + // Deliver next step to kod object by objectid + /*******************************************************************/ + + // unset possible heuristic flags from earlier (unreachable) steps + Response->flags &= ~MF_AVOIDING; + Response->flags &= ~MF_CLOCKWISE; + + // try to do step suggested from path + if (BSPGetStepFromPath(&roomnode->data, path, &path->front()->Location, &path->back()->Location, &p, &Response->flags, Response->objectid)) + BSPInvokeMoveCallback(Response->objectid, MC_NEWPATH, Response->flags, &p); + + // use heuristic + else if (BSPGetStepFromHeuristic(&roomnode->data, &Response->s, &Response->e, &p, &Response->flags, Response->objectid)) + BSPInvokeMoveCallback(Response->objectid, MC_HEURISTIC, Response->flags, &p); + + // fully blocked + else + BSPInvokeMoveCallback(Response->objectid, MC_UNREACHABLE, Response->flags, &p); + } +} + +bool AStarDeliverNextResponse() +{ + astar_response* response = AStarResponses.dequeue(); + + if (!response) + return false; + + switch (response->responseType()) + { + case astar_response_type::AStarPathResponse: + AStarDeliverPathResponse((astar_pathresponse*)response); + break; + } + + delete response; + return true; +} + +void AStarEnqueueCommand(astar_command* Command) +{ + // send copy of command to each worker + for (int i = 0; i < THREADSCOUNT; i++) + AStar[i].Commands->enqueue(Command->clone()); + + // delete the template + delete Command; +} + +void AStarInitGC() +{ + // tell workers to pause + for (int i = 0; i < THREADSCOUNT; i++) + AStar[i].DoPause = true; + + // wait for workers to get in pause mode + // they will process all pending commands first + while (true) + { + bool allpaused = true; + + for (int i = 0; i < THREADSCOUNT; i++) + if (!AStar[i].IsPaused) + allpaused = false; + + if (allpaused) + break; + } + + // clear the query queue, just discard them due to + // invalid (ids) and monsters get their waiting flag reset on GC + while (astar_query* query = AStarQueries.dequeue()) + delete query; + + // clear the response queues, free mem of pathnodes we not gonna use + while (astar_response* response = AStarResponses.dequeue()) + { + if (response->responseType() == AStarPathResponse) + { + astar_pathresponse* pathresponse = (astar_pathresponse*)response; + while (!pathresponse->path.empty()) + { + astar_path_node* node = pathresponse->path.front(); + pathresponse->path.pop_front(); + free(node); + } + } + delete response; + } +} + +void AStarFinishGC() +{ + // tell workers to run again + for (int i = 0; i < THREADSCOUNT; i++) + AStar[i].DoPause = false; +} + +void AStarPerformGC() +{ + // loop through all workers + for (int i = 0; i < THREADSCOUNT; i++) + { + astar* a = &AStar[i]; + + // loop through all rooms of this worker + for (std::map::iterator it = a->Rooms->begin(); it != a->Rooms->end(); ++it) + { + room_type* bsproom = it->second->Room; + Blocker* b = bsproom->Blocker; + + // loop through all blockers and update id + while (b) + { + b->ObjectID = GetObjectByID(b->ObjectID)->garbage_ref; + b = b->Next; + } + } + } +} +#pragma endregion \ No newline at end of file diff --git a/blakserv/astar.h b/blakserv/astar.h index bcb32a28fc..9e24443066 100644 --- a/blakserv/astar.h +++ b/blakserv/astar.h @@ -9,112 +9,199 @@ * astar.h: */ -#include - #ifndef _ASTAR_H #define _ASTAR_H -#define EDGESCACHEENABLED 1 -#define PATHCACHEENABLED 1 -#define PATHCACHETOLERANCE 3 -#define PATHCACHESIZE 16 -#define CLOSEENOUGHDIST 3 -#define DESTBLOCKIGNORE 3 -#define ASTARENABLED 1 -#define NUMNEIGHBOURS 8 - -// biggest known grid so far is desertshore3.roo -// having 391.680 squares. -#define MAXGRIDROWS 1024 -#define MAXGRIDCOLS 1024 -#define NODESDATASQUARES MAXGRIDROWS * MAXGRIDCOLS - -#define LCHILD(x) (2 * x + 1) -#define RCHILD(x) (2 * x + 2) -#define PARENT(x) ((x-1) / 2) - -#define COST 1.0f -#define COST_DIAG ((float)M_SQRT2) - -#define EDGECACHE_KNOWS_N 0x0001 -#define EDGECACHE_KNOWS_NE 0x0002 -#define EDGECACHE_KNOWS_E 0x0004 -#define EDGECACHE_KNOWS_SE 0x0008 -#define EDGECACHE_KNOWS_S 0x0010 -#define EDGECACHE_KNOWS_SW 0x0020 -#define EDGECACHE_KNOWS_W 0x0040 -#define EDGECACHE_KNOWS_NW 0x0080 -#define EDGECACHE_CAN_N 0x0101 -#define EDGECACHE_CAN_NE 0x0202 -#define EDGECACHE_CAN_E 0x0404 -#define EDGECACHE_CAN_SE 0x0808 -#define EDGECACHE_CAN_S 0x1010 -#define EDGECACHE_CAN_SW 0x2020 -#define EDGECACHE_CAN_W 0x4040 -#define EDGECACHE_CAN_NW 0x8080 +#include +#include +#include +#include + +/**************************************************************************************************************/ +/* GENERAL */ +/**************************************************************************************************************/ + +#define CLOSEENOUGHDIST 3 // allowed offset in squares to consider a square to be the end +#define DESTBLOCKIGNORE 3 // area around the end which never gets marked blocked by blockers + +/**************************************************************************************************************/ +/* GRID */ +/**************************************************************************************************************/ +#define COST 1.0f // cost of a straight-edge +#define COST_DIAG ((float)M_SQRT2) // cost of a diagonal-edge +#define MAXGRIDROWS 1024 // max. supported highres rows (1 highres = 16 fine) +#define MAXGRIDCOLS 1024 // max. supported highres cols (1 highres = 16 fine) +#define NODESDATASQUARES MAXGRIDROWS * MAXGRIDCOLS // max. supported highres-squares (rows*cols) + +/**************************************************************************************************************/ +/* PATH CACHE */ +/**************************************************************************************************************/ +#define PATHCACHESIZE 16 // how many paths are cached per room +#define PATHCACHEENDTOLERANCE 768.0f // max. distance between end and a cached end to be a match +#define PATHCACHESTARTTOLERANCE 16.0f // max. distance between start and a cached start to be a match + +/**************************************************************************************************************/ +/* NOPATH CACHE */ +/**************************************************************************************************************/ +#define NOPATHCACHESIZE 16 // how many unreachable start-end results are cached per room +#define NOPATHCACHEVALIDDURATIONINS 3 // how long a no-path cache entry is valid (in seconds) +#define NOPATHCACHEENDTOLERANCE 256.0f // max. distance between end and a cached end to be a match +#define NOPATHCACHESTARTTOLERANCE 256.0f // max. distance between start and a cached start to be a match + +/**************************************************************************************************************/ +/* THREADING */ +/**************************************************************************************************************/ +#define THREADSCOUNT 2 // how many astar workers (threads) are used +#define COMMANDQUEUESIZE 3000 // max. elements in command-queues (must be big, e.g. to sync room changes) +#define QUERYQUEUESIZE 100 // max. elements in query-queue +#define RESPONSEQUEUESIZE 100 // max. elements in response-queue +#define WORKERSLEEPTIMEMS 10 // sleep time for workers if they should sleep + +/**************************************************************************************************************/ +/* EDGE CACHE FLAGS */ +/**************************************************************************************************************/ + +#define EDGECACHE_KNOWS_N 0x0001 // set if north-edge is cached +#define EDGECACHE_KNOWS_NE 0x0002 // set if north-east-edge is cached +#define EDGECACHE_KNOWS_E 0x0004 // set if east-edge is cached +#define EDGECACHE_KNOWS_SE 0x0008 // set if south-east-edge is cached +#define EDGECACHE_KNOWS_S 0x0010 // set if south-edge is cached +#define EDGECACHE_KNOWS_SW 0x0020 // set if south-west-edge is cached +#define EDGECACHE_KNOWS_W 0x0040 // set if west-edge is cached +#define EDGECACHE_KNOWS_NW 0x0080 // set if north-west-edge is cached +#define EDGECACHE_CAN_N 0x0101 // set if north-edge is cached and walkable +#define EDGECACHE_CAN_NE 0x0202 // set if north-east-edge is cached and walkable +#define EDGECACHE_CAN_E 0x0404 // set if east-edge is cached and walkable +#define EDGECACHE_CAN_SE 0x0808 // set if south-east-edge is cached and walkable +#define EDGECACHE_CAN_S 0x1010 // set if south-edge is cached and walkable +#define EDGECACHE_CAN_SW 0x2020 // set if south-west-edge is cached and walkable +#define EDGECACHE_CAN_W 0x4040 // set if west-edge is cached and walkable +#define EDGECACHE_CAN_NW 0x8080 // set if north-west-edge is cached and walkable + +/**************************************************************************************************************/ +/* FORWARD DECLARATIONS */ +/**************************************************************************************************************/ typedef struct room_type room_type; typedef struct BspLeaf BspLeaf; -typedef struct astar_node_data astar_node_data; typedef struct astar_node astar_node; +/**************************************************************************************************************/ +/* STRUCTS */ +/**************************************************************************************************************/ + typedef struct astar_node_data { - float cost; - float heuristic; - float combined; - astar_node* parent; - astar_node* heapslot; - int heapindex; - bool isInClosedList; - bool isBlocked; + float cost; // cost of that node + float heuristic; // heuristic cost of that node + float combined; // sum of cost and heuristic + astar_node* parent; // parent (one of the neighbours) which provides the shortest path to the node + int heapindex; // index of this node in the heap + bool isInClosedList; // true for any evaluated node + bool isBlocked; // true for any node blocked by a blocker + astar_node* heapslot; // provides the n-th memory slot of the heap (unrelated other values/the node) } astar_node_data; +/**************************************************************************************************************/ + typedef struct astar_node_meta { - int Row; - int Col; - V2 Location; + int Row; // row of the node (in highres) + int Col; // column of the node (in highres) + V2 Location; // location (in ROO fineness) } astar_node_meta; +/**************************************************************************************************************/ + typedef struct astar_node { - astar_node_meta* Meta; - astar_node_data* Data; - BspLeaf* Leaf; -#if EDGESCACHEENABLED - unsigned short* Edges; -#endif + astar_node_meta* Meta; // the common meta data (equal for all nodes of any grid) + astar_node_data* Data; // the data-slot linked with this node + BspLeaf* Leaf; // a possible BSP leaf at the node's location + unsigned short* Edges; // pointer to the edge-cache of this node } astar_node; -class astar_path : public ::std::list +/**************************************************************************************************************/ + +typedef struct astar_path_node { -}; + int Row; // row of the node (in highres) + int Col; // col of the node (in highres) + V2 Location; // location (in ROO fineness) +} astar_path_node; + +/**************************************************************************************************************/ + +typedef struct astar_room +{ + room_type* Room; // the basic bsp room-data + astar_node** Grid; // the astar-grid of this room + unsigned short* EdgesCache; // the edges-cache of this room + int EdgesCacheSize; // size of the edges-cache in bytes +} astar_room; + +/**************************************************************************************************************/ + +typedef struct astar_nopath +{ + V2 S; + V2 E; + ::std::chrono::system_clock::time_point Tick; +} astar_nopath; + +/**************************************************************************************************************/ typedef struct astar { - astar_node_data* NodesData; - int NodesDataSize; - astar_node_meta** Grid; - astar_node* StartNode; - astar_node* EndNode; - astar_node* LastNode; - int ObjectID; - int HeapSize; - room_type* Room; + ::std::thread* Thread; // the thread using this struct instance + ::std::atomic IsRunning; // true if the worker is working + ::std::atomic IsPaused; // true if the worker is paused + ::std::atomic DoPause; // flip to true to pause this worker (may take some time) + astar_node_data* NodesData; // the nodes-data memory used by this worker + int NodesDataSize; // sie of nodesdata in bytes + astar_node_meta** Grid; // the meta-grid used by this worker + astar_node* StartNode; // start-node of a path-calculation + astar_node* EndNode; // end-node of a path-calculation + astar_node* LastNode; // holds the last-node on a path (null if unreachable) + int ObjectID; // object id we're calculating a path for + int HeapSize; // current size of the open-heap + astar_room* Room; // the room we calculate a path in + astar_rooms* Rooms; // all room intances this worker is using + astar_command_queue* Commands; // holds pending commands supposed to be processed by worker + ::std::atomic Memory; } astar; +/**************************************************************************************************************/ +/* WORKER AND QUEUE INSTANCES */ +/**************************************************************************************************************/ + +extern astar AStar[THREADSCOUNT]; // the astar-workers +extern astar_query_queue AStarQueries; // the query-queue to send tasks to the workers +extern astar_response_queue AStarResponses; // the response-queue to retrieve answers from workers + +/**************************************************************************************************************/ +/* C FUNCTIONS */ +/**************************************************************************************************************/ + void AStarInit(); void AStarShutdown(); -void AStarGenerateGrid(room_type* Room); -void AStarFreeGrid(room_type* Room); -bool AStarGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); - -#if EDGESCACHEENABLED -void AStarClearEdgesCache(room_type* Room); -#endif -#if PATHCACHEENABLED -void AStarClearPathCache(room_type* Room); -#endif +bool AStarDeliverNextResponse(); +void AStarEnqueueCommand(astar_command* Command); +void AStarInitGC(); +void AStarFinishGC(); +void AStarPerformGC(); + +__inline void* AStarAlloc(astar* Astar, unsigned int Size) +{ + void* mem = malloc(Size); + Astar->Memory += Size; + return mem; +} + +__inline void AStarFree(astar* Astar, void* Mem, unsigned int Size) +{ + free(Mem); + Astar->Memory -= Size; +} #endif /*#ifndef _ASTAR_H */ \ No newline at end of file diff --git a/blakserv/astar_classes.h b/blakserv/astar_classes.h new file mode 100644 index 0000000000..61924ba331 --- /dev/null +++ b/blakserv/astar_classes.h @@ -0,0 +1,359 @@ +// Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +// All rights reserved. +// +// This software is distributed under a license that is described in +// the LICENSE file that accompanies it. +// +// Meridian is a registered trademark. +/* +* astar_classes.h: Classes used by astar, mainly for the queue items. +* +*/ + +#ifndef _ASTAR_CLASSES__H +#define _ASTAR_CLASSES__H + +#include +#include + +/*********************************************************************************************************************/ + +typedef struct astar_node astar_node; +typedef struct astar_path_node astar_path_node; +typedef struct astar_room astar_room; + +/*********************************************************************************************************************/ + +class astar_path : public ::std::list { }; + +/*********************************************************************************************************************/ + +class astar_rooms : public ::std::map { }; + +/*********************************************************************************************************************/ +/* ENUMS */ +/*********************************************************************************************************************/ + +typedef enum astar_command_type +{ + AStarLoadRoom = 1, + AStarUnloadRoom = 2, + AStarBlockerAdd = 3, + AStarBlockerRemove = 4, + AStarBlockerMove = 5, + AStarBlockerClear = 6, + AStarMoveSector = 7, + AStarChangeTexture = 8 +} astar_command_type; + +typedef enum astar_query_type +{ + AStarPathQuery = 1 +} astar_query_type; + +typedef enum astar_response_type +{ + AStarPathResponse = 1 +} astar_response_type; + +/*********************************************************************************************************************/ +/* COMMANDS */ +/*********************************************************************************************************************/ + +class astar_command +{ +public: + virtual astar_command_type commandType() = 0; + virtual astar_command* clone() = 0; +}; + +/*********************************************************************************************************************/ + +class astar_command_loadroom : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarLoadRoom; } + + char* file; + int id; + + astar_command_loadroom(char* file, int id) + { + astar_command_loadroom::file = strdup(file); + astar_command_loadroom::id = id; + } + + ~astar_command_loadroom() + { + free(file); + } + + virtual astar_command* clone() override + { + return new astar_command_loadroom(file, id); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_unloadroom : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarUnloadRoom; } + + int id; + + astar_command_unloadroom(int id) + { + astar_command_unloadroom::id = id; + } + + virtual astar_command* clone() override + { + return new astar_command_unloadroom(id); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_blockeradd : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarBlockerAdd; } + + int roomid; + int objectid; + V2 p; + + astar_command_blockeradd(int roomid, int objectid, V2 p) + { + astar_command_blockeradd::roomid = roomid; + astar_command_blockeradd::objectid = objectid; + astar_command_blockeradd::p = p; + } + + virtual astar_command* clone() override + { + return new astar_command_blockeradd(roomid, objectid, p); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_blockerremove : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarBlockerRemove; } + + int roomid; + int objectid; + + astar_command_blockerremove(int roomid, int objectid) + { + astar_command_blockerremove::roomid = roomid; + astar_command_blockerremove::objectid = objectid; + } + + virtual astar_command* clone() override + { + return new astar_command_blockerremove(roomid, objectid); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_blockermove : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarBlockerMove; } + + int roomid; + int objectid; + V2 p; + + astar_command_blockermove(int roomid, int objectid, V2 p) + { + astar_command_blockermove::roomid = roomid; + astar_command_blockermove::objectid = objectid; + astar_command_blockermove::p = p; + } + + virtual astar_command* clone() override + { + return new astar_command_blockermove(roomid, objectid, p); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_blockerclear : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarBlockerClear; } + + int roomid; + + astar_command_blockerclear(int roomid) + { + astar_command_blockerclear::roomid = roomid; + } + + virtual astar_command* clone() override + { + return new astar_command_blockerclear(roomid); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_movesector : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarMoveSector; } + + int roomid; + unsigned int serverid; + bool floor; + float height; + float speed; + + astar_command_movesector(int roomid, unsigned int serverid, bool floor, float height, float speed) + { + astar_command_movesector::roomid = roomid; + astar_command_movesector::serverid = serverid; + astar_command_movesector::floor = floor; + astar_command_movesector::height = height; + astar_command_movesector::speed = speed; + } + + virtual astar_command* clone() override + { + return new astar_command_movesector(roomid, serverid, floor, height, speed); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_changetexture : public astar_command +{ +public: + virtual astar_command_type commandType() override { return astar_command_type::AStarChangeTexture; } + + int roomid; + unsigned int serverid; + unsigned short newtexture; + unsigned int flags; + + astar_command_changetexture(int roomid, unsigned int serverid, unsigned short newtexture, unsigned int flags) + { + astar_command_changetexture::roomid = roomid; + astar_command_changetexture::serverid = serverid; + astar_command_changetexture::newtexture = newtexture; + astar_command_changetexture::flags = flags; + } + + virtual astar_command* clone() override + { + return new astar_command_changetexture(roomid, serverid, newtexture, flags); + } +}; + +/*********************************************************************************************************************/ + +class astar_command_queue : public threadsafe_queue +{ +public: + astar_command_queue(int size) : threadsafe_queue(size) + { + } +}; + +/*********************************************************************************************************************/ +/* QUERIES */ +/*********************************************************************************************************************/ + +class astar_query +{ +public: + virtual astar_query_type queryType() = 0; +}; + + +/*********************************************************************************************************************/ + +class astar_pathquery : public astar_query +{ +public: + virtual astar_query_type queryType() override { return astar_query_type::AStarPathQuery; } + + int roomid; + int objectid; + V2 s; + V2 e; + unsigned int flags; + + astar_pathquery(int roomid, int objectid, V2 s, V2 e, unsigned int flags) + { + astar_pathquery::roomid = roomid; + astar_pathquery::objectid = objectid; + astar_pathquery::s = s; + astar_pathquery::e = e; + astar_pathquery::flags = flags; + } +}; + +/*********************************************************************************************************************/ + +class astar_query_queue : public threadsafe_queue +{ +public: + astar_query_queue(int size) : threadsafe_queue(size) + { + } +}; + +/*********************************************************************************************************************/ +/* RESPONSES */ +/*********************************************************************************************************************/ + +class astar_response +{ +public: + virtual astar_response_type responseType() = 0; +}; + +/*********************************************************************************************************************/ + +class astar_pathresponse : public astar_response +{ +public: + virtual astar_response_type responseType() override { return astar_response_type::AStarPathResponse; } + + int roomid; + int objectid; + V2 s; + V2 e; + unsigned int flags; + astar_path path; + + astar_pathresponse(int roomid, int objectid, V2 s, V2 e, unsigned int flags) + { + astar_pathresponse::roomid = roomid; + astar_pathresponse::objectid = objectid; + astar_pathresponse::s = s; + astar_pathresponse::e = e; + astar_pathresponse::flags = flags; + } +}; + +/*********************************************************************************************************************/ + +class astar_response_queue : public threadsafe_queue +{ +public: + astar_response_queue(int size) : threadsafe_queue(size) + { + } +}; + +#endif /*#ifndef _ASTAR_CLASSES__H */ \ No newline at end of file diff --git a/blakserv/blakserv.h b/blakserv/blakserv.h index df3c4535e4..20cf5c8911 100644 --- a/blakserv/blakserv.h +++ b/blakserv/blakserv.h @@ -78,7 +78,14 @@ enum REALTIME_CLASS = 36, EVENTENGINE_CLASS = 37, ESCAPED_CONVICT_CLASS = 38, - MAX_BUILTIN_CLASS = 38 + MOVECALLBACK_MSG = 39, + IROW_PARM = 40, + ICOL_PARM = 41, + IFINEROW_PARM = 42, + IFINECOL_PARM = 43, + ITYPE_PARM = 44, + IFLAGS_PARM = 45, + MAX_BUILTIN_CLASS = 45 // To add other C-accessible KOD identifiers, // see the BLAKCOMP's table of BuiltinIds[]. @@ -234,6 +241,7 @@ char * GetLastErrorStr(); #define WM_BLAK_MAIN_DELETE_ACCOUNT (WM_APP + 4002) #define WM_BLAK_MAIN_VERIFIED_LOGIN (WM_APP + 4003) #define WM_BLAK_MAIN_LOAD_GAME (WM_APP + 4004) +#define WM_BLAK_MAIN_PATH_READY (WM_APP + 4005) #include "bof.h" @@ -242,6 +250,7 @@ char * GetLastErrorStr(); #include "stringinthash.h" #include "intstringhash.h" +#include "queue.h" #include "geometry.h" #include "blakres.h" @@ -260,6 +269,7 @@ char * GetLastErrorStr(); #include "system.h" #include "loadrsc.h" #include "loadgame.h" +#include "astar_classes.h" #include "astar.h" #include "roofile.h" #include "roomdata.h" diff --git a/blakserv/blakserv.vcxproj b/blakserv/blakserv.vcxproj index fba89e0c3b..3251df85f1 100644 --- a/blakserv/blakserv.vcxproj +++ b/blakserv/blakserv.vcxproj @@ -192,6 +192,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server + @@ -233,6 +234,7 @@ copy $(SolutionDir)bin\libcurl.dll $(SolutionDir)run\server + diff --git a/blakserv/blakserv.vcxproj.filters b/blakserv/blakserv.vcxproj.filters index 7e16fa6f6d..d90611228e 100644 --- a/blakserv/blakserv.vcxproj.filters +++ b/blakserv/blakserv.vcxproj.filters @@ -233,6 +233,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/blakserv/ccode.c b/blakserv/ccode.c index 4ca14aca08..8b76855b55 100644 --- a/blakserv/ccode.c +++ b/blakserv/ccode.c @@ -2889,9 +2889,13 @@ int C_ChangeTextureBSP(int object_id, local_var_type *local_vars, return ret_val.int_val; } - BSPChangeTexture(&r->data, (unsigned short)server_id.v.data, + BSPChangeTexture(&r->data, (unsigned int)server_id.v.data, (unsigned short)new_texnum.v.data, flags.v.data); + // also change texture in astar workers + AStarEnqueueCommand( + new astar_command_changetexture(r->data.roomdata_id, (unsigned int)server_id.v.data, (unsigned short)new_texnum.v.data, (unsigned int)flags.v.data)); + return ret_val.int_val; } @@ -2964,6 +2968,10 @@ int C_MoveSectorBSP(int object_id, local_var_type *local_vars, BSPMoveSector(&r->data, (unsigned int)server_id.v.data, is_floor, fheight, fspeed); + // also move sector in astar workers + AStarEnqueueCommand( + new astar_command_movesector(r->data.roomdata_id, (unsigned int)server_id.v.data, is_floor, fheight, fspeed)); + return ret_val.int_val; } @@ -3047,6 +3055,9 @@ int C_BlockerAddBSP(int object_id, local_var_type *local_vars, // query ret_val.v.data = BSPBlockerAdd(&r->data, obj_val.v.data, &p); + // also add to astar workers + AStarEnqueueCommand(new astar_command_blockeradd(r->data.roomdata_id, obj_val.v.data, p)); + return ret_val.int_val; } @@ -3130,6 +3141,9 @@ int C_BlockerMoveBSP(int object_id, local_var_type *local_vars, // query ret_val.v.data = BSPBlockerMove(&r->data, obj_val.v.data, &p); + // also move in astar workers + AStarEnqueueCommand(new astar_command_blockermove(r->data.roomdata_id, obj_val.v.data, p)); + return ret_val.int_val; } @@ -3172,6 +3186,9 @@ int C_BlockerRemoveBSP(int object_id, local_var_type *local_vars, // query ret_val.v.data = BSPBlockerRemove(&r->data, obj_val.v.data); + // also remove in astar workers + AStarEnqueueCommand(new astar_command_blockerremove(r->data.roomdata_id, obj_val.v.data)); + return ret_val.int_val; } @@ -3205,6 +3222,9 @@ int C_BlockerClearBSP(int object_id, local_var_type *local_vars, // query BSPBlockerClear(&r->data); + // also clear in astar workers + AStarEnqueueCommand(new astar_command_blockerclear(r->data.roomdata_id)); + return ret_val.int_val; } @@ -3436,31 +3456,14 @@ int C_GetStepTowardsBSP(int object_id, local_var_type *local_vars, s.Y = GRIDCOORDTOROO(row_source.v.data, finerow_source.v.data); V2 e; - e.X = GRIDCOORDTOROO(local_vars->locals[col_dest.v.data].v.data, local_vars->locals[finecol_dest.v.data].v.data); - e.Y = GRIDCOORDTOROO(local_vars->locals[row_dest.v.data].v.data, local_vars->locals[finerow_dest.v.data].v.data); + e.X = GRIDCOORDTOROO(col_dest.v.data, finecol_dest.v.data); + e.Y = GRIDCOORDTOROO(row_dest.v.data, finerow_dest.v.data); - V2 p; unsigned int flags = (unsigned int)state_flags.v.data; - bool ok = BSPGetStepTowards(&r->data, &s, &e, &p, &flags, objectid.v.data); - - if (ok) - { - ret_val.v.tag = TAG_INT; - ret_val.v.data = flags; - - local_vars->locals[finecol_dest.v.data].v.tag = TAG_INT; - local_vars->locals[finecol_dest.v.data].v.data = ROOCOORDTOGRIDFINE(p.X); - - local_vars->locals[finerow_dest.v.data].v.tag = TAG_INT; - local_vars->locals[finerow_dest.v.data].v.data = ROOCOORDTOGRIDFINE(p.Y); - - local_vars->locals[col_dest.v.data].v.tag = TAG_INT; - local_vars->locals[col_dest.v.data].v.data = ROOCOORDTOGRIDBIG(p.X); - - local_vars->locals[row_dest.v.data].v.tag = TAG_INT; - local_vars->locals[row_dest.v.data].v.data = ROOCOORDTOGRIDBIG(p.Y); - } + BSPGetStepTowards(&r->data, &s, &e, flags, objectid.v.data); + ret_val.v.tag = TAG_INT; + ret_val.v.data = 1; return ret_val.int_val; } diff --git a/blakserv/garbage.c b/blakserv/garbage.c index 3ce4061974..8252c329e9 100644 --- a/blakserv/garbage.c +++ b/blakserv/garbage.c @@ -96,10 +96,14 @@ int next_table_renumber; void GarbageCollect() { + /* anyone in game mode w/o a user can have stale data, so knock 'em out */ ForEachSession(GarbageKickoffGamePick); ForEachSession(GarbageWarnAdminSession); + + // wait for astar workers to pause + AStarInitGC(); UpdateSecurityRedbook(); @@ -113,6 +117,8 @@ void GarbageCollect() // Tables now get GC'd, so don't reset them. //ResetTables(); + + /* First, garbage collect the list nodes and tables */ /* @@ -205,6 +211,8 @@ void GarbageCollect() ForEachObject(RenumberObject); // Renumber object IDs in each room's blocker data. ForEachRoom(RenumberBlockerObjects); + // Renumber object IDs in astar + AStarPerformGC(); ForEachObject(RenumberObjectReferences); // Also mark strings here ForEachListNode(RenumberListNodeObjectReferences); // Also mark strings here ForEachTable(RenumberTableObjectReferences); // Also mark strings here @@ -236,6 +244,9 @@ void GarbageCollect() SetNumTimers(next_timer_renumber); ForEachString(CompactString); SetNumStrings(next_string_renumber); + + // continue all astar workers + AStarFinishGC(); } ///////////////////////////////////////////////////////////////////////////// diff --git a/blakserv/memory.c b/blakserv/memory.c index 3b1dfdd80c..1b56c56825 100644 --- a/blakserv/memory.c +++ b/blakserv/memory.c @@ -275,6 +275,11 @@ void InitMemory(void) memory_statistics * GetMemoryStats(void) { + // update current value from astar first + memory_stat.allocated[MALLOC_ID_ASTAR] = 0; + for (int i = 0; i < THREADSCOUNT; i++) + memory_stat.allocated[MALLOC_ID_ASTAR] += AStar[i].Memory; + return &memory_stat; } @@ -282,6 +287,11 @@ int GetMemoryTotal(void) { int i,total; + // update current value from astar first + memory_stat.allocated[MALLOC_ID_ASTAR] = 0; + for (int i = 0; i < THREADSCOUNT; i++) + memory_stat.allocated[MALLOC_ID_ASTAR] += AStar[i].Memory; + total = 0; for (i=0;i +#include + +template +class threadsafe_queue +{ +private: + ::std::queue queue; + ::std::mutex mutex; + unsigned int maxsize; + +public: + threadsafe_queue(int size) + { + maxsize = size; + } + + ~threadsafe_queue() + { + } + + bool enqueue(T item) + { + bool ret = false; + + mutex.lock(); + if (queue.size() < maxsize) + { + ret = true; + queue.push(item); + } + mutex.unlock(); + + return ret; + } + + T dequeue() + { + T ret = NULL; + + mutex.lock(); + if (!queue.empty()) + { + ret = queue.front(); + queue.pop(); + } + mutex.unlock(); + + return ret; + } + + bool empty() + { + bool ret = true; + + mutex.lock(); + ret = queue.empty(); + mutex.unlock(); + + return ret; + } +}; +#endif /*#ifndef _QUEUE_H */ diff --git a/blakserv/roofile.c b/blakserv/roofile.c index 4f5504205b..a26fd02555 100644 --- a/blakserv/roofile.c +++ b/blakserv/roofile.c @@ -599,6 +599,35 @@ bool BSPCanMoveInRoomTree(BspNode* Node, V2* S, V2* E, Wall** BlockWall) return BSPCanMoveInRoomTree(Node->u.internal.LeftChild, S, E, BlockWall); } } + +void BSPClearPath(astar_path* Path) +{ + while (!Path->empty()) + { + astar_path_node* pathnode = Path->front(); + Path->pop_front(); + free(pathnode); + } +} + +void BSPClearNoPath(astar_nopath* NoPath) +{ + NoPath->S = { 0.0f, 0.0f }; + NoPath->E = { 0.0f, 0.0f }; + NoPath->Tick = ::std::chrono::time_point<::std::chrono::high_resolution_clock, ::std::chrono::milliseconds>(); +} + +void BSPClearPathCache(room_type* Room) +{ + for (int i = 0; i < PATHCACHESIZE; i++) + BSPClearPath(Room->Paths[i]); +} + +void BSPClearNoPathCache(room_type* Room) +{ + for (int i = 0; i < NOPATHCACHESIZE; i++) + BSPClearNoPath(Room->NoPaths[i]); +} #pragma endregion #pragma region Public @@ -607,6 +636,102 @@ bool BSPCanMoveInRoomTree(BspNode* Node, V2* S, V2* E, Wall** BlockWall) /* These are defined in header and can be called from outside */ /**************************************************************************************************************/ +bool BSPGetStepFromPath(room_type* Room, astar_path* Path, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +{ + V2 d; + Wall* blockWall; + + // a valid path would have at least two entries + if (Path->size() < 2) + return false; + + // check start (better match almost exactly, or we might stepping back?) + astar_path_node* first = Path->front(); + V2SUB(&d, &first->Location, S); + if (V2LEN2(&d) > PATHCACHESTARTTOLERANCE*PATHCACHESTARTTOLERANCE) + return false; + + // check end (has small tolerance) + astar_path_node* last = Path->back(); + V2SUB(&d, &last->Location, E); + if (V2LEN2(&d) > PATHCACHEENDTOLERANCE*PATHCACHEENDTOLERANCE) + return false; + + // match! now remove first, so we also match on next step + Path->pop_front(); + + // get the next step endpoint + astar_path_node* next = Path->front(); + + // make sure we can still move to this next endpoint (cares for moved objects!) + // note: if objects block a cached path, the path will still be walked until the block occurs! + // to improve this revalidate the whole path from the first to the last node here + if (!BSPCanMoveInRoom(Room, &first->Location, &next->Location, ObjectID, false, &blockWall, false)) + return false; + + // for diagonal moves mark to be long step (required for timer elapse) + if (abs(first->Col - next->Col) && + abs(first->Row - next->Row)) + { + *Flags |= MF_LONG_STEP; + } + else + *Flags &= ~MF_LONG_STEP; + + // set step endpoint + *P = next->Location; + + // delete removed entry + free(first); + + // cache hit + return true; +} + +bool BSPGetStepFromPathCache(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +{ + for (int i = 0; i < PATHCACHESIZE; i++) + if (BSPGetStepFromPath(Room, Room->Paths[i], S, E, P, Flags, ObjectID)) + return true; + + // no cache hit + return false; +} + +bool BSPGetStepFromNoPath(astar_nopath* NoPath, V2* S, V2* E) +{ + V2 d; + + // must match start + V2SUB(&d, &NoPath->S, S); + if (V2LEN2(&d) > NOPATHCACHESTARTTOLERANCE*NOPATHCACHESTARTTOLERANCE) + return false; + + // must match end + V2SUB(&d, &NoPath->E, E); + if (V2LEN2(&d) > NOPATHCACHEENDTOLERANCE*NOPATHCACHEENDTOLERANCE) + return false; + + // must not be too long ago + ::std::chrono::system_clock::time_point now = + ::std::chrono::high_resolution_clock::now(); + + if (now - NoPath->Tick > ::std::chrono::seconds(NOPATHCACHEVALIDDURATIONINS)) + return false; + + return true; +} + +bool BSPGetStepFromNoPathCache(room_type* Room, V2* S, V2* E) +{ + for (int i = 0; i < NOPATHCACHESIZE; i++) + if (BSPGetStepFromNoPath(Room->NoPaths[i], S, E)) + return true; + + // no cache hit + return false; +} + /*********************************************************************************************/ /* BSPGetHeight: Returns true if location is inside any sector, false otherwise. /* If true, heights are in parameters HeightF (floor), @@ -673,7 +798,7 @@ bool BSPCanMoveInRoom(room_type* Room, V2* S, V2* E, int ObjectID, bool moveOuts V2 ms; // from m to s V2SUB(&ms, S, &blocker->Position); float ds2 = V2LEN2(&ms); - + // CASE 1) Start is too close // Note: IntersectLineCircle below will reject moves starting or ending exactly // on the circle as well as moves going from inside to outside of the circle. @@ -759,15 +884,9 @@ void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short New } } - // must invalidate astar caches -#if ASTARENABLED -#if EDGESCACHEENABLED - AStarClearEdgesCache(Room); -#endif -#if PATHCACHEENABLED - AStarClearPathCache(Room); -#endif -#endif + // must invalidate astar pathcache attached to room + BSPClearPathCache(Room); + BSPClearNoPathCache(Room); } /*********************************************************************************************/ @@ -799,15 +918,9 @@ void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Hei } } - // must invalidate astar caches -#if ASTARENABLED -#if EDGESCACHEENABLED - AStarClearEdgesCache(Room); -#endif -#if PATHCACHEENABLED - AStarClearPathCache(Room); -#endif -#endif + // must invalidate astar pathcache attached to room + BSPClearPathCache(Room); + BSPClearNoPathCache(Room); } /*********************************************************************************************/ @@ -950,22 +1063,64 @@ bool BSPGetRandomPoint(room_type* Room, int MaxAttempts, V2* P) } /*********************************************************************************************/ -/* BSPGetStepTowards: Returns a location in P param, in a distant of 16 kod fineness units -/* away from S on the way towards E. +/* BSPInvokeMoveCallback: Delivers a next-step response to the requesting KOD object /*********************************************************************************************/ -bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +void BSPInvokeMoveCallback(int ObjectID, int Type, unsigned int Flags, V2* P) { - if (!Room || !S || !E || !P || !Flags) - return false; - - // Monsters that can move through walls or outside the tree will - // send this flag with state. - bool moveOutsideBSP = ((*Flags & MSTATE_MOVE_OUTSIDE_BSP) == MSTATE_MOVE_OUTSIDE_BSP); - - // but must not give these back in piState - *Flags &= ~MSTATE_MOVE_OUTSIDE_BSP; + val_type vals[6]; + vals[0].v.tag = TAG_INT; + vals[0].v.data = Type; + vals[1].v.tag = TAG_INT; + vals[1].v.data = Flags; + vals[2].v.tag = TAG_INT; + vals[2].v.data = ROOCOORDTOGRIDBIG(P->Y); + vals[3].v.tag = TAG_INT; + vals[3].v.data = ROOCOORDTOGRIDBIG(P->X); + vals[4].v.tag = TAG_INT; + vals[4].v.data = ROOCOORDTOGRIDFINE(P->Y); + vals[5].v.tag = TAG_INT; + vals[5].v.data = ROOCOORDTOGRIDFINE(P->X); + + parm_node parms[6]; + parms[0].type = CONSTANT; + parms[0].name_id = ITYPE_PARM; + parms[0].value = vals[0].int_val; + + parms[1].type = CONSTANT; + parms[1].name_id = IFLAGS_PARM; + parms[1].value = vals[1].int_val; + + parms[2].type = CONSTANT; + parms[2].name_id = IROW_PARM; + parms[2].value = vals[2].int_val; + + parms[3].name_id = ICOL_PARM; + parms[3].type = CONSTANT; + parms[3].value = vals[3].int_val; + + parms[4].name_id = IFINEROW_PARM; + parms[4].type = CONSTANT; + parms[4].value = vals[4].int_val; + + parms[5].name_id = IFINECOL_PARM; + parms[5].type = CONSTANT; + parms[5].value = vals[5].int_val; + + SendBlakodMessage(ObjectID, MOVECALLBACK_MSG, 6, parms); +} +/*********************************************************************************************/ +/* BSPGetStepFromHeuristic: Determines a new point to step to by an heuristic. +/*********************************************************************************************/ +bool BSPGetStepFromHeuristic(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID) +{ + Wall* blockWall = NULL; V2 se, stepend; + + bool isAvoiding = ((*Flags & MF_AVOIDING) == MF_AVOIDING); + bool isLeft = ((*Flags & MF_CLOCKWISE) == MF_CLOCKWISE); + + // delta vector from start to end V2SUB(&se, E, S); // get length from start to end @@ -974,10 +1129,9 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags // trying to step to old location? if (ISZERO(len)) { - // set step destination to end - *P = *E; - *Flags &= ~ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; + *P = *S; + *Flags &= ~MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; return true; } @@ -988,277 +1142,345 @@ bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags // apply the scale on se V2SCALE(&se, scale); - /****************************************************/ - // 1) test direct line towards destination first - /****************************************************/ - Wall* blockWall = NULL; + /**********************************************************************************/ + // first try a step straight towards destination - if (BSPCanMoveInRoom(Room, S, E, ObjectID, moveOutsideBSP, &blockWall, false)) + V2ADD(&stepend, S, &se); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) { - // note: we must verify the location the object is actually going to end up in KOD, - // this means we must round to the next closer kod-fineness value, - // so these values are also exactly expressable in kod coordinates. - // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units - V2ADD(&stepend, S, &se); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags &= ~ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + *P = stepend; + *Flags &= ~MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; } - /****************************************************/ - // 2) can't do direct step - /****************************************************/ + /**********************************************************************************/ + // determine step by heuristic - // try get next step from astar path if enabled - if (ASTARENABLED && AStarGetStepTowards(Room, S, E, P, Flags, ObjectID)) + // not yet in clockwise or cclockwise mode + if (!isAvoiding) { - *Flags &= ~ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; + // if not blocked by a wall, roll a dice to decide + // how to get around the blocking obj. + if (!blockWall) + isLeft = (rand() % 2 == 1); + + // blocked by wall, go first into 'slide-along' direction + // based on vector towards target + else + { + V2 p1p2; + V2SUB(&p1p2, &blockWall->P2, &blockWall->P1); + + // note: walls can be aligned in any direction like left->right, right->left, + // same with up->down and same also with the movement vector. + // The typical angle between vectors, acosf(..) is therefore insufficient to differ. + // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis) + // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped + // to the left or right decision. + float f1 = atan2f(se.Y, se.X); + float f2 = atan2f(p1p2.Y, p1p2.X); + float df = f1 - f2; + + bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2); + bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI); + bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2)); + bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f); + bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2); + bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI); + bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2)); + bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f); + + isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true; + } } - else - { - bool isAvoiding = ((*Flags & ESTATE_AVOIDING) == ESTATE_AVOIDING); - bool isLeft = ((*Flags & ESTATE_CLOCKWISE) == ESTATE_CLOCKWISE); - // not yet in clockwise or cclockwise mode - if (!isAvoiding) + /**********************************************************************************/ + + // must run this possibly twice + // e.g. left after right failed or right after left failed + for (int i = 0; i < 2; i++) + { + if (isLeft) { - // if not blocked by a wall, roll a dice to decide - // how to get around the blocking obj. - if (!blockWall) - isLeft = (rand() % 2 == 1); + V2 v = se; + + // try 22.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; + } - // blocked by wall, go first into 'slide-along' direction - // based on vector towards target - else + // try 45° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) { - V2 p1p2; - V2SUB(&p1p2, &blockWall->P2, &blockWall->P1); - - // note: walls can be aligned in any direction like left->right, right->left, - // same with up->down and same also with the movement vector. - // The typical angle between vectors, acosf(..) is therefore insufficient to differ. - // What is done here is a convert into polar-coordinates (= angle in 0..2pi from x-axis) - // The difference (or sum) (-2pi..2pi) then provides up to 8 different cases (quadrants) which must be mapped - // to the left or right decision. - float f1 = atan2f(se.Y, se.X); - float f2 = atan2f(p1p2.Y, p1p2.X); - float df = f1 - f2; - - bool q1_pos = (df >= 0.0f && df <= (float)M_PI_2); - bool q2_pos = (df >= (float)M_PI_2 && df <= (float)M_PI); - bool q3_pos = (df >= (float)M_PI && df <= (float)(M_PI + M_PI_2)); - bool q4_pos = (df >= (float)(M_PI + M_PI_2) && df <= (float)M_PI*2.0f); - bool q1_neg = (df <= 0.0f && df >= (float)-M_PI_2); - bool q2_neg = (df <= (float)-M_PI_2 && df >= (float)-M_PI); - bool q3_neg = (df <= (float)-M_PI && df >= (float)-(M_PI + M_PI_2)); - bool q4_neg = (df <= (float)-(M_PI + M_PI_2) && df >= (float)-M_PI*2.0f); - - isLeft = (q1_pos || q2_pos || q1_neg || q3_neg) ? false : true; - - /*if (isLeft) - dprintf("trying left first r: %f", df); - else - dprintf("trying right first r: %f", df);*/ + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; } - } - // must run this possibly twice - // e.g. left after right failed or right after left failed - for (int i = 0; i < 2; i++) - { - if (isLeft) + // try 67.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) { - V2 v = se; - - // try 22.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; + } - // try 45° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 90° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; + } - // try 67.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 112.5° left + V2ROTATE(&v, 0.5f * (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; + } - // try 90° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 135° left + V2ROTATE(&v, (float)-M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + return true; + } - // try 112.5° left - V2ROTATE(&v, 0.5f * (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // failed to circumvent by going left, switch to right + isLeft = false; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + } + else + { + V2 v = se; + + // try 22.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; + } - // try 135° left - V2ROTATE(&v, (float)-M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - return true; - } + // try 45° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; + } - // failed to circumvent by going left, switch to right - isLeft = false; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; + // try 67.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; } - else + + // try 90° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) { - V2 v = se; - - // try 22.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; + } - // try 45° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + // try 112.5° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; + } - // try 67.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + // try 135° right + V2ROTATE(&v, 0.5f * (float)M_PI_4); + V2ADD(&stepend, S, &v); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, false, &blockWall, false)) + { + *P = stepend; + *Flags |= MF_AVOIDING; + *Flags &= ~MF_CLOCKWISE; + return true; + } - // try 90° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + // failed to circumvent by going right, switch to left + isLeft = true; + *Flags |= MF_AVOIDING; + *Flags |= MF_CLOCKWISE; + } + } + return false; +} - // try 112.5° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } +/*********************************************************************************************/ +/* BSPGetStepTowards: +/*********************************************************************************************/ +void BSPGetStepTowards(room_type* Room, V2* S, V2* E, unsigned int Flags, int ObjectID) +{ + Wall* blockWall = NULL; - // try 135° right - V2ROTATE(&v, 0.5f * (float)M_PI_4); - V2ADD(&stepend, S, &v); - stepend.X = ROUNDROOTOKODFINENESS(stepend.X); - stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); - if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) - { - *P = stepend; - *Flags |= ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return true; - } + if (!Room || !S || !E) + return; - // failed to circumvent by going right, switch to left - isLeft = true; - *Flags |= ESTATE_AVOIDING; - *Flags |= ESTATE_CLOCKWISE; - } + // some monsters can move through walls or outside the tree + bool moveOutsideBSP = ((Flags & MF_MOVE_OUTSIDE_BSP) == MF_MOVE_OUTSIDE_BSP); + + V2 se, stepend; + V2SUB(&se, E, S); + + // get length from start to end + float len = V2LEN(&se); + + // trying to step to old location? + if (ISZERO(len)) + { + BSPInvokeMoveCallback(ObjectID, MC_STRAIGHTLINE, Flags, S); + return; + } + + // this first normalizes the se vector, + // then scales to a length of 16 kod-fineunits (=256 roo-fineunits) + float scale = (1.0f / len) * FINENESSKODTOROO(16.0f); + + // apply the scale on se + V2SCALE(&se, scale); + + /****************************************************/ + // 1) try step on straight line towards destination + // Note: Blockers are not important on the long line, only short + /****************************************************/ + if (BSPCanMoveInRoom(Room, S, E, ObjectID, moveOutsideBSP, &blockWall, true)) + { + // note: we must verify the location the object is actually going to end up in KOD, + // this means we must round to the next closer kod-fineness value, + // so these values are also exactly expressable in kod coordinates. + // in fact this makes the vector a variable length between ~15.5 and ~16.5 fine units + V2ADD(&stepend, S, &se); + stepend.X = ROUNDROOTOKODFINENESS(stepend.X); + stepend.Y = ROUNDROOTOKODFINENESS(stepend.Y); + if (BSPCanMoveInRoom(Room, S, &stepend, ObjectID, moveOutsideBSP, &blockWall, false)) + { + BSPInvokeMoveCallback(ObjectID, MC_STRAIGHTLINE, Flags, &stepend); + return; } } /****************************************************/ - // 3) fully stuck + // 2) step from path cache /****************************************************/ + if (BSPGetStepFromPathCache(Room, S, E, &stepend, &Flags, ObjectID)) + { + BSPInvokeMoveCallback(ObjectID, MC_PATHFROMCACHE, Flags, &stepend); + return; + } - *P = *S; - *Flags &= ~ESTATE_AVOIDING; - *Flags &= ~ESTATE_CLOCKWISE; - return false; + /****************************************************/ + // 3) get step from nopath (unreachable) cache + /****************************************************/ + if (BSPGetStepFromNoPathCache(Room, S, E)) + { + // step by heuristic + if (BSPGetStepFromHeuristic(Room, S, E, &stepend, &Flags, ObjectID)) + { + BSPInvokeMoveCallback(ObjectID, MC_HEURISTIC, Flags, &stepend); + return; + } + + // full block + else + { + stepend = { 0.0f, 0.0f }; + BSPInvokeMoveCallback(ObjectID, MC_UNREACHABLE, Flags, &stepend); + return; + } + } + + /****************************************************/ + // 4) calculate new path async + /****************************************************/ + astar_pathquery* query = new astar_pathquery(Room->roomdata_id, ObjectID, *S, *E, Flags); + if (!AStarQueries.enqueue(query)) + { + // failed because full, fall back to heuristic + if (BSPGetStepFromHeuristic(Room, S, E, &stepend, &Flags, ObjectID)) + BSPInvokeMoveCallback(ObjectID, MC_HEURISTIC, Flags, &stepend); + + // full block + else + { + stepend = { 0.0f, 0.0f }; + BSPInvokeMoveCallback(ObjectID, MC_UNREACHABLE, Flags, &stepend); + } + } } /*********************************************************************************************/ @@ -1270,7 +1492,7 @@ void BSPBlockerClear(room_type* Room) while (blocker) { Blocker* tmp = blocker->Next; - FreeMemory(MALLOC_ID_ROOM, blocker, sizeof(Blocker)); + free(blocker); blocker = tmp; } Room->Blocker = NULL; @@ -1300,7 +1522,7 @@ bool BSPBlockerRemove(room_type* Room, int ObjectID) previous->Next = blocker->Next; // now cleanup node - FreeMemory(MALLOC_ID_ROOM, blocker, sizeof(Blocker)); + free(blocker); return true; } @@ -1321,7 +1543,7 @@ bool BSPBlockerAdd(room_type* Room, int ObjectID, V2* P) return false; // alloc - Blocker* newblocker = (Blocker*)AllocateMemory(MALLOC_ID_ROOM, sizeof(Blocker)); + Blocker* newblocker = (Blocker*)malloc(sizeof(Blocker)); // set values on new blocker newblocker->ObjectID = ObjectID; @@ -1369,7 +1591,7 @@ bool BSPBlockerMove(room_type* Room, int ObjectID, V2* P) /*********************************************************************************************/ /* BSPRooFileLoadServer: Fill "room" with server-relevant data from given roo file. */ /*********************************************************************************************/ -bool BSPLoadRoom(char *fname, room_type *room) +bool BSPLoadRoom(char *fname, room_type *room, astar* Astar) { int i, j, temp; unsigned char byte; @@ -1451,8 +1673,9 @@ bool BSPLoadRoom(char *fname, room_type *room) { fclose(infile); return False; } // allocate tree mem - room->TreeNodes = (BspNode*)AllocateMemory( - MALLOC_ID_ROOM, room->TreeNodesCount * sizeof(BspNode)); + room->TreeNodes = (Astar) ? + (BspNode*)AStarAlloc(Astar, room->TreeNodesCount * sizeof(BspNode)) : + (BspNode*)AllocateMemory(MALLOC_ID_ROOM, room->TreeNodesCount * sizeof(BspNode)); for (i = 0; i < room->TreeNodesCount; i++) { @@ -1504,10 +1727,13 @@ bool BSPLoadRoom(char *fname, room_type *room) { fclose(infile); return False; } // allocate memory for points of polygon - node->u.leaf.PointsFloor = (V3*)AllocateMemory( - MALLOC_ID_ROOM, node->u.leaf.PointsCount * sizeof(V3)); - node->u.leaf.PointsCeiling = (V3*)AllocateMemory( - MALLOC_ID_ROOM, node->u.leaf.PointsCount * sizeof(V3)); + node->u.leaf.PointsFloor = (Astar) ? + (V3*)AStarAlloc(Astar, node->u.leaf.PointsCount * sizeof(V3)) : + (V3*)AllocateMemory(MALLOC_ID_ROOM, node->u.leaf.PointsCount * sizeof(V3)); + + node->u.leaf.PointsCeiling = (Astar) ? + (V3*)AStarAlloc(Astar, node->u.leaf.PointsCount * sizeof(V3)) : + (V3*)AllocateMemory(MALLOC_ID_ROOM, node->u.leaf.PointsCount * sizeof(V3)); // read points for (j = 0; j < node->u.leaf.PointsCount; j++) @@ -1533,8 +1759,9 @@ bool BSPLoadRoom(char *fname, room_type *room) { fclose(infile); return False; } // allocate walls mem - room->Walls = (Wall*)AllocateMemory( - MALLOC_ID_ROOM, room->WallsCount * sizeof(Wall)); + room->Walls = (Astar) ? + (Wall*)AStarAlloc(Astar, room->WallsCount * sizeof(Wall)) : + (Wall*)AllocateMemory(MALLOC_ID_ROOM, room->WallsCount * sizeof(Wall)); for (i = 0; i < room->WallsCount; i++) { @@ -1593,8 +1820,9 @@ bool BSPLoadRoom(char *fname, room_type *room) { fclose(infile); return False; } // allocate sides mem - room->Sides = (Side*)AllocateMemory( - MALLOC_ID_ROOM, room->SidesCount * sizeof(Side)); + room->Sides = (Astar) ? + (Side*)AStarAlloc(Astar, room->SidesCount * sizeof(Side)) : + (Side*)AllocateMemory(MALLOC_ID_ROOM, room->SidesCount * sizeof(Side)); for (i = 0; i < room->SidesCount; i++) { @@ -1635,8 +1863,9 @@ bool BSPLoadRoom(char *fname, room_type *room) { fclose(infile); return False; } // allocate sectors mem - room->Sectors = (Sector*)AllocateMemory( - MALLOC_ID_ROOM, room->SectorsCount * sizeof(Sector)); + room->Sectors = (Astar) ? + (Sector*)AStarAlloc(Astar, room->SectorsCount * sizeof(Sector)) : + (Sector*)AllocateMemory(MALLOC_ID_ROOM, room->SectorsCount * sizeof(Sector)); for (i = 0; i < room->SectorsCount; i++) { @@ -1685,8 +1914,9 @@ bool BSPLoadRoom(char *fname, room_type *room) // possibly load floor slopeinfo if ((sector->Flags & SF_SLOPED_FLOOR) == SF_SLOPED_FLOOR) { - sector->SlopeInfoFloor = (SlopeInfo*)AllocateMemory( - MALLOC_ID_ROOM, sizeof(SlopeInfo)); + sector->SlopeInfoFloor = (Astar) ? + (SlopeInfo*)AStarAlloc(Astar, sizeof(SlopeInfo)) : + (SlopeInfo*)AllocateMemory(MALLOC_ID_ROOM, sizeof(SlopeInfo)); // read 3d plane equation coefficients (normal vector) if (fread(§or->SlopeInfoFloor->A, 1, 4, infile) != 4) @@ -1716,8 +1946,9 @@ bool BSPLoadRoom(char *fname, room_type *room) // possibly load ceiling slopeinfo if ((sector->Flags & SF_SLOPED_CEILING) == SF_SLOPED_CEILING) { - sector->SlopeInfoCeiling = (SlopeInfo*)AllocateMemory( - MALLOC_ID_ROOM, sizeof(SlopeInfo)); + sector->SlopeInfoCeiling = (Astar) ? + (SlopeInfo*)AStarAlloc(Astar, sizeof(SlopeInfo)) : + (SlopeInfo*)AllocateMemory(MALLOC_ID_ROOM, sizeof(SlopeInfo)); // read 3d plane equation coefficients (normal vector) if (fread(§or->SlopeInfoCeiling->A, 1, 4, infile) != 4) @@ -1852,43 +2083,43 @@ bool BSPLoadRoom(char *fname, room_type *room) // bsp nodes for (int i = 0; i < room->TreeNodesCount; i++) { - BspNode* node = &room->TreeNodes[i]; - - // internal nodes - if (node->Type == BspInternalType) - { - // first wall - if (node->u.internal.FirstWallNum > 0 && - room->WallsCount > node->u.internal.FirstWallNum - 1) - node->u.internal.FirstWall = &room->Walls[node->u.internal.FirstWallNum - 1]; - else - node->u.internal.FirstWall = NULL; - - // right child - if (node->u.internal.RightChildNum > 0 && - room->TreeNodesCount > node->u.internal.RightChildNum - 1) - node->u.internal.RightChild = &room->TreeNodes[node->u.internal.RightChildNum - 1]; - else - node->u.internal.RightChild = NULL; - - // left child - if (node->u.internal.LeftChildNum > 0 && - room->TreeNodesCount > node->u.internal.LeftChildNum - 1) - node->u.internal.LeftChild = &room->TreeNodes[node->u.internal.LeftChildNum - 1]; - else - node->u.internal.LeftChild = NULL; - } - - // leafs - else if (node->Type == BspLeafType) - { - // sector this leaf belongs to - if (node->u.leaf.SectorNum > 0 && - room->SectorsCount > node->u.leaf.SectorNum - 1) - node->u.leaf.Sector = &room->Sectors[node->u.leaf.SectorNum - 1]; - else - node->u.leaf.Sector = NULL; - } + BspNode* node = &room->TreeNodes[i]; + + // internal nodes + if (node->Type == BspInternalType) + { + // first wall + if (node->u.internal.FirstWallNum > 0 && + room->WallsCount > node->u.internal.FirstWallNum - 1) + node->u.internal.FirstWall = &room->Walls[node->u.internal.FirstWallNum - 1]; + else + node->u.internal.FirstWall = NULL; + + // right child + if (node->u.internal.RightChildNum > 0 && + room->TreeNodesCount > node->u.internal.RightChildNum - 1) + node->u.internal.RightChild = &room->TreeNodes[node->u.internal.RightChildNum - 1]; + else + node->u.internal.RightChild = NULL; + + // left child + if (node->u.internal.LeftChildNum > 0 && + room->TreeNodesCount > node->u.internal.LeftChildNum - 1) + node->u.internal.LeftChild = &room->TreeNodes[node->u.internal.LeftChildNum - 1]; + else + node->u.internal.LeftChild = NULL; + } + + // leafs + else if (node->Type == BspLeafType) + { + // sector this leaf belongs to + if (node->u.leaf.SectorNum > 0 && + room->SectorsCount > node->u.leaf.SectorNum - 1) + node->u.leaf.Sector = &room->Sectors[node->u.leaf.SectorNum - 1]; + else + node->u.leaf.Sector = NULL; + } } /*************************************************************************/ @@ -1897,34 +2128,36 @@ bool BSPLoadRoom(char *fname, room_type *room) for (int i = 0; i < room->TreeNodesCount; i++) { - BspNode* node = &room->TreeNodes[i]; + BspNode* node = &room->TreeNodes[i]; - if (node->Type != BspLeafType) - continue; + if (node->Type != BspLeafType) + continue; - for (int j = 0; j < node->u.leaf.PointsCount; j++) - { - if (!node->u.leaf.Sector) - continue; + for (int j = 0; j < node->u.leaf.PointsCount; j++) + { + if (!node->u.leaf.Sector) + continue; - V2 p = { node->u.leaf.PointsFloor[j].X, node->u.leaf.PointsFloor[j].Y }; + V2 p = { node->u.leaf.PointsFloor[j].X, node->u.leaf.PointsFloor[j].Y }; - node->u.leaf.PointsFloor[j].Z = - SECTORHEIGHTFLOOR(node->u.leaf.Sector, &p); + node->u.leaf.PointsFloor[j].Z = + SECTORHEIGHTFLOOR(node->u.leaf.Sector, &p); - node->u.leaf.PointsCeiling[j].Z = - SECTORHEIGHTCEILING(node->u.leaf.Sector, &p); - } + node->u.leaf.PointsCeiling[j].Z = + SECTORHEIGHTCEILING(node->u.leaf.Sector, &p); + } } - /*************************************************************************/ - /* GENERATE ASTAR PERSISTENT DATA */ - /*************************************************************************/ + /****************************************************************************/ - AStarGenerateGrid(room); + room->NextPathIdx = 0; + room->NextNoPathIdx = 0; - /****************************************************************************/ - /****************************************************************************/ + for (int i = 0; i < PATHCACHESIZE; i++) + room->Paths[i] = new astar_path(); + + for (int i = 0; i < NOPATHCACHESIZE; i++) + room->NoPaths[i] = new astar_nopath(); // no initial blockers room->Blocker = NULL; @@ -1935,23 +2168,35 @@ bool BSPLoadRoom(char *fname, room_type *room) /*********************************************************************************************/ /* BSPRoomFreeServer: Free the parts of a room structure used by the server. */ /*********************************************************************************************/ -void BSPFreeRoom(room_type *room) +void BSPFreeRoom(room_type *room, astar* Astar) { int i; /****************************************************************************/ /* CLIENT PARTS */ /****************************************************************************/ - + // free bsp nodes 'submem' for (i = 0; i < room->TreeNodesCount; i++) { if (room->TreeNodes[i].Type == BspLeafType) { - FreeMemory(MALLOC_ID_ROOM, room->TreeNodes[i].u.leaf.PointsFloor, - room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); - FreeMemory(MALLOC_ID_ROOM, room->TreeNodes[i].u.leaf.PointsCeiling, - room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); + if (Astar) + { + AStarFree(Astar, room->TreeNodes[i].u.leaf.PointsFloor, + room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); + + AStarFree(Astar, room->TreeNodes[i].u.leaf.PointsCeiling, + room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); + } + else + { + FreeMemory(MALLOC_ID_ROOM, room->TreeNodes[i].u.leaf.PointsFloor, + room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); + + FreeMemory(MALLOC_ID_ROOM, room->TreeNodes[i].u.leaf.PointsCeiling, + room->TreeNodes[i].u.leaf.PointsCount * sizeof(V3)); + } } } @@ -1959,15 +2204,34 @@ void BSPFreeRoom(room_type *room) for (i = 0; i < room->SectorsCount; i++) { if ((room->Sectors[i].Flags & SF_SLOPED_FLOOR) == SF_SLOPED_FLOOR) - FreeMemory(MALLOC_ID_ROOM, room->Sectors[i].SlopeInfoFloor, sizeof(SlopeInfo)); - if ((room->Sectors[i].Flags & SF_SLOPED_CEILING) == SF_SLOPED_CEILING) - FreeMemory(MALLOC_ID_ROOM, room->Sectors[i].SlopeInfoCeiling, sizeof(SlopeInfo)); + { + (Astar) ? + AStarFree(Astar, room->Sectors[i].SlopeInfoFloor, sizeof(SlopeInfo)) : + FreeMemory(MALLOC_ID_ROOM, room->Sectors[i].SlopeInfoFloor, sizeof(SlopeInfo)); + } + + if ((room->Sectors[i].Flags & SF_SLOPED_CEILING) == SF_SLOPED_CEILING) + { + (Astar) ? + AStarFree(Astar, room->Sectors[i].SlopeInfoCeiling, sizeof(SlopeInfo)) : + FreeMemory(MALLOC_ID_ROOM, room->Sectors[i].SlopeInfoCeiling, sizeof(SlopeInfo)); + } } - FreeMemory(MALLOC_ID_ROOM, room->TreeNodes, room->TreeNodesCount * sizeof(BspNode)); - FreeMemory(MALLOC_ID_ROOM, room->Walls, room->WallsCount * sizeof(Wall)); - FreeMemory(MALLOC_ID_ROOM, room->Sides, room->SidesCount * sizeof(Side)); - FreeMemory(MALLOC_ID_ROOM, room->Sectors, room->SectorsCount * sizeof(Sector)); + if (Astar) + { + AStarFree(Astar, room->TreeNodes, room->TreeNodesCount * sizeof(BspNode)); + AStarFree(Astar, room->Walls, room->WallsCount * sizeof(Wall)); + AStarFree(Astar, room->Sides, room->SidesCount * sizeof(Side)); + AStarFree(Astar, room->Sectors, room->SectorsCount * sizeof(Sector)); + } + else + { + FreeMemory(MALLOC_ID_ROOM, room->TreeNodes, room->TreeNodesCount * sizeof(BspNode)); + FreeMemory(MALLOC_ID_ROOM, room->Walls, room->WallsCount * sizeof(Wall)); + FreeMemory(MALLOC_ID_ROOM, room->Sides, room->SidesCount * sizeof(Side)); + FreeMemory(MALLOC_ID_ROOM, room->Sectors, room->SectorsCount * sizeof(Sector)); + } room->TreeNodesCount = 0; room->WallsCount = 0; @@ -1975,8 +2239,13 @@ void BSPFreeRoom(room_type *room) room->SectorsCount = 0; BSPBlockerClear(room); + BSPClearPathCache(room); + + for (int i = 0; i < PATHCACHESIZE; i++) + delete room->Paths[i]; - AStarFreeGrid(room); + for (int i = 0; i < NOPATHCACHESIZE; i++) + delete room->NoPaths[i]; /****************************************************************************/ /* SERVER PARTS */ diff --git a/blakserv/roofile.h b/blakserv/roofile.h index 2724a818bb..3b53877ca4 100644 --- a/blakserv/roofile.h +++ b/blakserv/roofile.h @@ -50,13 +50,11 @@ // value exactly expressable in KOD fineness units #define ROUNDROOTOKODFINENESS(a) FINENESSKODTOROO(roundf(FINENESSROOTOKOD(a))) -// from blakston.khd, used in BSPGetNextStepTowards across calls -#define ESTATE_LONG_STEP 0x00002000 -#define ESTATE_AVOIDING 0x00004000 -#define ESTATE_CLOCKWISE 0x00008000 - // from blakston.khd, used for monster that can move outside BSP tree -#define MSTATE_MOVE_OUTSIDE_BSP 0x00100000 +#define MF_MOVE_OUTSIDE_BSP 0x00100000 +#define MF_LONG_STEP 0x00200000 +#define MF_AVOIDING 0x00400000 +#define MF_CLOCKWISE 0x00800000 // query flags for BSPGetLocationInfo #define LIQ_GET_SECTORINFO 0x00000001 @@ -82,6 +80,17 @@ /**************************************************************************************************************/ /* STRUCTS */ /**************************************************************************************************************/ + +// must match blakston.khd +typedef enum MoveCallbackType +{ + MC_UNREACHABLE = 0, + MC_STRAIGHTLINE = 1, + MC_PATHFROMCACHE = 2, + MC_NEWPATH = 3, + MC_HEURISTIC = 4 +} MoveCallbackType; + typedef struct BoundingBox2D { V2 Min; @@ -211,15 +220,11 @@ typedef struct room_type Sector* Sectors; unsigned short SectorsCount; - astar_node** Grid; -#if EDGESCACHEENABLED - unsigned short* EdgesCache; - int EdgesCacheSize; -#endif -#if PATHCACHEENABLED - astar_path* Paths[PATHCACHESIZE]; - unsigned int NextPathIdx; -#endif + astar_path* Paths[PATHCACHESIZE]; + unsigned int NextPathIdx; + + astar_nopath* NoPaths[NOPATHCACHESIZE]; + unsigned int NextNoPathIdx; } room_type; #pragma endregion @@ -234,13 +239,18 @@ void BSPChangeTexture(room_type* Room, unsigned int ServerID, unsigned short Ne void BSPMoveSector(room_type* Room, unsigned int ServerID, bool Floor, float Height, float Speed); bool BSPGetLocationInfo(room_type* Room, V2* P, unsigned int QueryFlags, unsigned int* ReturnFlags, float* HeightF, float* HeightFWD, float* HeightC, BspLeaf** Leaf); bool BSPGetRandomPoint(room_type* Room, int MaxAttempts, V2* P); -bool BSPGetStepTowards(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); +void BSPGetStepTowards(room_type* Room, V2* S, V2* E, unsigned int Flags, int ObjectID); +bool BSPGetStepFromHeuristic(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); +bool BSPGetStepFromPath(room_type* Room, astar_path* Path, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); +bool BSPGetStepFromPathCache(room_type* Room, V2* S, V2* E, V2* P, unsigned int* Flags, int ObjectID); bool BSPBlockerAdd(room_type* Room, int ObjectID, V2* P); bool BSPBlockerMove(room_type* Room, int ObjectID, V2* P); bool BSPBlockerRemove(room_type* Room, int ObjectID); void BSPBlockerClear(room_type* Room); -bool BSPLoadRoom(char *fname, room_type *room); -void BSPFreeRoom(room_type *room); +bool BSPLoadRoom(char* fname, room_type* room, astar* Astar); +void BSPFreeRoom(room_type* room, astar* Astar); +void BSPInvokeMoveCallback(int ObjectID, int Type, unsigned int State, V2* P); +void BSPClearPath(astar_path* Path); #pragma endregion #endif diff --git a/blakserv/roomdata.c b/blakserv/roomdata.c index 611b79d575..160e9f7f02 100644 --- a/blakserv/roomdata.c +++ b/blakserv/roomdata.c @@ -72,8 +72,11 @@ void ResetRooms() room = rooms[i % INIT_ROOMTABLE_SIZE]; while (room) { + // send command to unload from astar + AStarEnqueueCommand(new astar_command_unloadroom(room->data.roomdata_id)); + temp = room->next; - BSPFreeRoom(&room->data); + BSPFreeRoom(&room->data, NULL); FreeMemory(MALLOC_ID_ROOM, room, sizeof(room_node)); room = temp; } @@ -128,7 +131,7 @@ int LoadRoom(int resource_id) sprintf(s, "%s%s", ConfigStr(PATH_ROOMS), r->resource_val[0]); // try load it - if (!BSPLoadRoom(s, &newnode->data)) + if (!BSPLoadRoom(s, &newnode->data, NULL)) { FreeMemory(MALLOC_ID_ROOM, newnode, sizeof(room_node)); bprintf("LoadRoomData couldn't open %s!!!\n",r->resource_val[0]); @@ -150,6 +153,10 @@ int LoadRoom(int resource_id) room_rscs[resource_id % INIT_ROOMTABLE_SIZE] = rrsc; ret_val.v.data = newnode->data.roomdata_id; + + // also load room in astar workers, using roomdata_id for mapping + AStarEnqueueCommand(new astar_command_loadroom(s, newnode->data.roomdata_id)); + return ret_val.int_val; } @@ -192,7 +199,10 @@ void UnloadRoom(room_node *r) temp = room->next; if (room->data.roomdata_id == r->data.roomdata_id) { - BSPFreeRoom(&room->data); + // send command to unload from astar workers + AStarEnqueueCommand(new astar_command_unloadroom(room->data.roomdata_id)); + + BSPFreeRoom(&room->data, NULL); room = temp; rooms[r->data.roomdata_id % INIT_ROOMTABLE_SIZE] = temp; return; diff --git a/blakserv/timer.c b/blakserv/timer.c index 72be62bfc7..103f035a40 100644 --- a/blakserv/timer.c +++ b/blakserv/timer.c @@ -348,7 +348,7 @@ void ServiceTimers(void) if (ms > 500) ms = 500; } - + if (MsgWaitForMultipleObjects(0,NULL,0,(DWORD)ms,QS_ALLINPUT) == WAIT_OBJECT_0) { while (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) @@ -389,6 +389,11 @@ void ServiceTimers(void) LoadFromKod(msg.lParam); LeaveServerLock(); break; + case WM_BLAK_MAIN_PATH_READY: + EnterServerLock(); + AStarDeliverNextResponse(); + LeaveServerLock(); + break; default : dprintf("ServiceTimers got unknown message %i\n",msg.message); diff --git a/kod/include/blakston.khd b/kod/include/blakston.khd index e0b37dec85..819bb99513 100644 --- a/kod/include/blakston.khd +++ b/kod/include/blakston.khd @@ -158,6 +158,13 @@ LIR_SECTOR_HASCTEX = 0x000040 LIR_BLOCKED_OBJECT = 0x000100 + % Types used in MoveCallback (must match roofile.h) + MC_UNREACHABLE = 0 + MC_STRAIGHTLINE = 1 + MC_PATHFROMCACHE = 2 + MC_NEWPATH = 3 + MC_HEURISTIC = 4 + %%% Projectile and Light flags % Flags for spells/projectiles @@ -1619,11 +1626,13 @@ VSTATE_INVALID_ATTACK = 0x20000 VSTATE_VALIDITY_MASK = 0x0FFFF - % Flag passed to BSP C calls to signify the monster - % can move outside the BSP tree. Mobs that can do this - % have the AI_MOVE_WALKTHROUGH_WALLS AI flag. - MSTATE_MOVE_OUTSIDE_BSP = 0x100000 - + % Flags passed to BSP GetStepTowards calls + % These are based on others + MF_MOVE_OUTSIDE_BSP = 0x100000 + MF_LONG_STEP = 0x200000 + MF_AVOIDING = 0x400000 + MF_CLOCKWISE = 0x800000 + % brain identification numbers BRAIN_ORIGINAL = 1 BRAIN_REVENANT = 2 @@ -1677,7 +1686,9 @@ AI_FIGHT_SWITCHALOT = 0x0800000 % monster may strike through walls (revenants) AI_FIGHT_THROUGH_WALLS = 0x1000000 - + % sometimes monsters need to wait a bit for next move available + AI_MOVE_WAITING_FOR_STEP = 0x2000000 + % Monster movement speeds SPEED_NONE = 0 SPEED_VERY_SLOW = 4 diff --git a/kod/object/active/holder/nomoveon/battler/monster.kod b/kod/object/active/holder/nomoveon/battler/monster.kod index f7df5c5d58..b97356281b 100644 --- a/kod/object/active/holder/nomoveon/battler/monster.kod +++ b/kod/object/active/holder/nomoveon/battler/monster.kod @@ -499,6 +499,11 @@ properties: % 20 seconds default for players, 60 for monsters piHurtMeTime = 60000 + pbNextMoveFaceTarget = FALSE + pbNextMoveFaceAway = FALSE + pbNextMoveToMaster = FALSE + poMoveStartedRoom = $ + messages: Constructor(template=FALSE, color=0, piSurvivalLevel=0, iBrain = 0) @@ -1643,7 +1648,7 @@ messages: { lBehavior = Cons(AI_LOOPING_PATROL,lBehavior); } - + piBehavior = viDefault_behavior; foreach i in lBehavior @@ -2534,17 +2539,19 @@ messages: if oTarget = $ { Debug("Bad info passed to MoveTowards!"); - return FALSE; - } - - return Send(self, @MoveTowardsCoords, - #iRow=Send(oTarget,@GetRow), - #iCol=Send(oTarget,@GetCol), - #iFineRow=Send(oTarget,@GetFineRow), - #iFineCol=Send(oTarget,@GetFineCol), - #face_target=face_target, - #face_away=face_away, - #to_master=to_master); + return; + } + + Send(self, @MoveTowardsCoords, + #iRow=Send(oTarget,@GetRow), + #iCol=Send(oTarget,@GetCol), + #iFineRow=Send(oTarget,@GetFineRow), + #iFineCol=Send(oTarget,@GetFineCol), + #face_target=face_target, + #face_away=face_away, + #to_master=to_master); + + return; } MoveAway(oTarget = $, face_target=FALSE, face_away=FALSE) @@ -2554,12 +2561,12 @@ messages: if oTarget = $ { Debug("Bad info passed to MoveAway!"); - return FALSE; + return; } if poOwner <> Send(oTarget,@GetOwner) { - return FALSE; + return; } % calculate square delta in fineness @@ -2570,13 +2577,15 @@ messages: dcol = dcol - ((Send(oTarget,@GetCol) * FINENESS) + Send(oTarget,@GetFineCol)); % add vector from oTarget to self on our position and try move there - return Send(self, @MoveTowardsCoords, - #iRow=(piRow + (drow / FINENESS)), - #iCol=(piCol + (dcol / FINENESS)), - #iFineRow=(piFine_row + (drow MOD FINENESS)), - #iFineCol=(piFine_col + (dcol MOD FINENESS)), - #face_target=face_target, - #face_away=face_away); + Send(self, @MoveTowardsCoords, + #iRow=(piRow + (drow / FINENESS)), + #iCol=(piCol + (dcol / FINENESS)), + #iFineRow=(piFine_row + (drow MOD FINENESS)), + #iFineCol=(piFine_col + (dcol MOD FINENESS)), + #face_target=face_target, + #face_away=face_away); + + return; } ReqMonsterMove(new_row = $,new_col = $,new_finerow = FINENESS_HALF, @@ -2650,44 +2659,115 @@ messages: MoveTowardsCoords(iRow=$, iCol=$, iFineRow=FINENESS_HALF, iFineCol=FINENESS_HALF, face_target=FALSE, face_away=FALSE, to_master=FALSE) { - local iState, iMoveFlags; + local iMoveFlags; if (iRow = $ or iCol = $) { return; } - % Mobs that can move outside the BSP tree set this field which is then - % combined with the state flags and sent to GetStepTowardsBSP. These - % moves aren't checked against room geometry. - iMoveFlags = 0; + % build flags for query here from different flag fields + iMoveFlags = 0; if (piBehavior & AI_MOVE_WALKTHROUGH_WALLS) { - iMoveFlags = iMoveFlags | MSTATE_MOVE_OUTSIDE_BSP; + iMoveFlags = iMoveFlags | MF_MOVE_OUTSIDE_BSP; } - + if (piState & ESTATE_AVOIDING) + { + iMoveFlags = iMoveFlags | MF_AVOIDING; + } + if (piState & ESTATE_CLOCKWISE) + { + iMoveFlags = iMoveFlags | MF_CLOCKWISE; + } + + % save some parameters of this move for the callback + pbNextMoveFaceTarget = face_target; + pbNextMoveFaceAway = face_away; + pbNextMoveToMaster = to_master; + poMoveStartedRoom = poOwner; + + % set flag on behaviour, unset in callback + piBehavior = piBehavior | AI_MOVE_WAITING_FOR_STEP; + % query where to step next % note: these need some persistent info across % calls which are stored in piState (flags from old KOD code) - iState = GetStepTowardsBSP( + GetStepTowardsBSP( Send(poOwner, @GetRoomData), piRow, piCol, piFine_row, piFine_col, - *iRow, *iCol, *iFineRow, *iFineCol, - piState | iMoveFlags, self); + iRow, iCol, iFineRow, iFineCol, + iMoveFlags, self); + + % may have no an answer here ready yet, or already processed, see MoveCallback + return; + } + + MoveCallback(iType=0, iFlags=0, iRow=0, iCol=0, iFineRow=0, iFineCol=0) + "Called from C when the next-step is available." + "This may happen during GetStepTowardsBSP() and therefore BEFORE" + "MoveTowardsCoords() ends, or at any later time if path needs to be calculated." + { + %debug("movecallback",iType,iFlags,iRow,iCol,iFineRow,iFineCol); + + % not waiting for a response anymore, e.g. serversave occured or others like + % we lost aggro on that target while path was calculated.. + if NOT (piBehavior & AI_MOVE_WAITING_FOR_STEP) + { + debug("Monster ", self, " received MoveCallback without waiting for it."); + return; + } + + % unset waiting flag on behaviour + piBehavior = piBehavior & ~AI_MOVE_WAITING_FOR_STEP; - if iState <> $ + % move return is invalid by now, because we changed rooms + if (poMoveStartedRoom <> poOwner OR poOwner = $) { - piState = iState; + % unset some flags + piState = piState & ~ESTATE_LONG_STEP; + piState = piState & ~ESTATE_AVOIDING; + piState = piState & ~ESTATE_CLOCKWISE; + return; + } - % debug("step",iRow,iCol,iFineRow,iFineCol); + % apply some returned move-flags on their original flag fields + if (iFlags & MF_LONG_STEP) + { + piState = piState | ESTATE_LONG_STEP; + } + else + { + piState = piState & ~ESTATE_LONG_STEP; + } + if (iFlags & MF_AVOIDING) + { + piState = piState | ESTATE_AVOIDING; + } + else + { + piState = piState & ~ESTATE_AVOIDING; + } + if (iFlags & MF_CLOCKWISE) + { + piState = piState | ESTATE_CLOCKWISE; + } + else + { + piState = piState & ~ESTATE_CLOCKWISE; + } + % all reachable types with valid position are bigger MC_UNREACHABLE + % right now we don't care what specifc kind + if iType > MC_UNREACHABLE + { if (NOT (piBehavior & AI_MOVE_WALKTHROUGH_WALLS)) OR Send(self,@ReqMonsterMove,#new_row=iRow,#new_col=iCol, #new_finerow=iFineRow,#new_finecol=iFineCol) { Send(self,@MonsterOrient,#new_row=iRow,#new_col=iCol, #new_finerow=iFineRow,#new_finecol=iFineCol, - #face_target=face_target,#face_away=face_away); + #face_target=pbNextMoveFaceTarget,#face_away=pbNextMoveFaceAway); Send(poOwner,@SomethingMoved,#what=self,#speed=viSpeed, #new_row=iRow,#new_col=iCol, @@ -2697,15 +2777,28 @@ messages: % Turn to face target Send(self,@MonsterOrient,#new_row=iRow,#new_col=iCol, #new_finerow=iFineRow,#new_finecol=iFineCol, - #face_target=face_target,#face_away=face_away); - - return TRUE; + #face_target=pbNextMoveFaceTarget,#face_away=pbNextMoveFaceAway); } } + else + { + % fully blocked: this is unlikely to happen + % because if no path -> move heuristc was used + } - return FALSE; + return; } + + GarbageCollecting() + "Unsets flags of pending moves across GC, because there" + "won't be any response." + { + % unset waiting flag on behaviour + piBehavior = piBehavior & ~AI_MOVE_WAITING_FOR_STEP; + return; + } + GotoCoords(iRow=$,iCol=$,iFineRow=FINENESS_HALF,iFineCol=FINENESS_HALF) "Move to a specific row and column. warning! fineness really" "isn't accurate as the monster will stop when it reaches the destination" diff --git a/kod/object/passive/brain.kod b/kod/object/passive/brain.kod index b0fc962313..53620293a0 100644 --- a/kod/object/passive/brain.kod +++ b/kod/object/passive/brain.kod @@ -564,6 +564,12 @@ messages: return; } + % waiting on a path response + if (behavior & AI_MOVE_WAITING_FOR_STEP) + { + return; + } + if (behavior & AI_HAS_TARGET_LOCATION) { Send(mob,@DoGoTowardsLocation); diff --git a/kod/util/system.kod b/kod/util/system.kod index 55042f1a71..0332982d7f 100644 --- a/kod/util/system.kod +++ b/kod/util/system.kod @@ -1001,6 +1001,8 @@ messages: Send(poStatistics,@GarbageCollecting); + Send(&Monster, @GarbageCollecting); + return; }