Support for v3 path files, removed the movement log stuff, fix for a crash or two

This commit is contained in:
KimLS 2017-07-27 23:21:45 -07:00
parent 69c6879ac9
commit 7d3f35d48b
5 changed files with 311 additions and 136 deletions

View File

@ -4382,9 +4382,6 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer;
Message(0, "Client Update Position (%.2f, %.2f, %.2f, %u) (%u)", ppu->x_pos, ppu->y_pos, ppu->z_pos, ppu->heading, ppu->animation);
Message(0, "Client Update Deltas (%.2f, %.2f, %.2f, %u)", ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading);
/* Boat handling */
if (ppu->spawn_id != GetID()) {
/* If player is controlling boat */

View File

@ -445,7 +445,7 @@ Mob::Mob(const char* in_name,
AssistAggro = false;
npc_assist_cap = 0;
PathRecalcTimer.reset(new Timer(1000));
PathRecalcTimer.reset(new Timer(2000));
}
Mob::~Mob()

View File

@ -5,12 +5,14 @@
#include <boost/graph/astar_search.hpp>
#include <string>
#include <memory>
#include <iostream>
#include <stdio.h>
#include "pathfinder_waypoint.h"
#include "zone.h"
#include "client.h"
#include "../common/eqemu_logsys.h"
#include "../common/string_util.h"
#include "../common/rulesys.h"
extern Zone *zone;
@ -36,12 +38,6 @@ struct PathFileHeader {
};
#pragma pack()
struct Node
{
glm::vec3 v;
float bestz;
};
struct Edge
{
float distance;
@ -49,6 +45,14 @@ struct Edge
int door_id;
};
struct Node
{
int id;
glm::vec3 v;
float bestz;
std::map<int, Edge> edges;
};
template <class Graph, class CostType, class NodeMap>
class distance_heuristic : public boost::astar_heuristic<Graph, CostType>
{
@ -85,7 +89,6 @@ private:
};
typedef boost::geometry::model::point<float, 3, boost::geometry::cs::cartesian> Point;
typedef boost::geometry::model::box<Point> Box;
typedef std::pair<Point, unsigned int> RTreeValue;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property,
boost::property<boost::edge_weight_t, float>> GraphType;
@ -96,50 +99,15 @@ struct PathfinderWaypoint::Implementation {
boost::geometry::index::rtree<RTreeValue, boost::geometry::index::quadratic<16>> Tree;
GraphType Graph;
std::vector<Node> Nodes;
std::map<std::pair<size_t, size_t>, Edge> Edges;
std::string FileName;
};
PathfinderWaypoint::PathfinderWaypoint(const std::string &path)
{
PathFileHeader Head;
m_impl.reset(new Implementation());
m_impl->PathFileValid = false;
Head.PathNodeCount = 0;
Head.version = 2;
FILE *f = fopen(path.c_str(), "rb");
if (f) {
char Magic[10];
fread(&Magic, 9, 1, f);
if (strncmp(Magic, "EQEMUPATH", 9))
{
Log(Logs::General, Logs::Error, "Bad Magic String in .path file.");
fclose(f);
return;
}
fread(&Head, sizeof(Head), 1, f);
Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld",
(long)Head.version, (long)Head.PathNodeCount);
if (Head.version == 2)
{
LoadV2(f, Head);
return;
}
else if (Head.version == 3) {
LoadV3(f, Head);
return;
}
else {
Log(Logs::General, Logs::Error, "Unsupported path file version.");
fclose(f);
return;
}
}
m_impl->FileName = path;
Load(path);
}
PathfinderWaypoint::~PathfinderWaypoint()
@ -151,21 +119,15 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g
std::vector<RTreeValue> result_start_n;
m_impl->Tree.query(boost::geometry::index::nearest(Point(start.x, start.y, start.z), 1), std::back_inserter(result_start_n));
if (result_start_n.size() == 0) {
IPath Route;
Route.push_back(start);
Route.push_back(end);
return Route;
return IPath();
}
std::vector<RTreeValue> result_end_n;
m_impl->Tree.query(boost::geometry::index::nearest(Point(end.x, end.y, end.z), 1), std::back_inserter(result_end_n));
if (result_end_n.size() == 0) {
IPath Route;
Route.push_back(start);
Route.push_back(end);
return Route;
return IPath();
}
auto &nearest_start = *result_start_n.begin();
auto &nearest_end = *result_end_n.begin();
@ -175,7 +137,7 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g
Route.push_back(end);
return Route;
}
std::vector<GraphType::vertex_descriptor> p(boost::num_vertices(m_impl->Graph));
try {
boost::astar_search(m_impl->Graph, nearest_start.second,
@ -194,8 +156,10 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g
break;
}
else {
auto iter = m_impl->Edges.find(std::make_pair(p[v], p[v + 1]));
if (iter != m_impl->Edges.end()) {
auto &node = m_impl->Nodes[v];
auto iter = node.edges.find(p[v + 1]);
if (iter != node.edges.end()) {
auto &edge = iter->second;
if (edge.teleport) {
Route.push_front(m_impl->Nodes[v].v);
@ -210,14 +174,14 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g
}
}
}
Route.push_front(start);
return Route;
}
IPath Route;
Route.push_back(start);
Route.push_back(end);
Route.push_front(start);
Route.push_back(glm::vec3(nearest_start.first.get<0>(), nearest_start.first.get<1>(), nearest_start.first.get<2>()));
return Route;
}
@ -231,95 +195,186 @@ void PathfinderWaypoint::DebugCommand(Client *c, const Seperator *sep)
if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help"))
{
c->Message(0, "Syntax: #path shownodes: Spawns a npc to represent every npc node.");
c->Message(0, "#path show: Plots a path from the user to their target.");
c->Message(0, "#path info node_id: Gives information about node info (requires shownode target).");
c->Message(0, "#path dump file_name: Dumps the current zone->pathing to a file of your naming.");
c->Message(0, "#path add [requested_id]: Adds a node at your current location will try to take the requested id if possible.");
c->Message(0, "#path connect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node and connects that node back (requires shownode target).");
c->Message(0, "#path sconnect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node (requires shownode target).");
c->Message(0, "#path qconnect [set]: short cut connect, connects the targeted node to the node you set with #path qconnect set (requires shownode target).");
c->Message(0, "#path disconnect [all]/disconnect_from_id: Disconnects the currently targeted node to disconnect from disconnect from id's node (requires shownode target), if passed all as the second argument it will disconnect this node from every other node.");
c->Message(0, "#path move: Moves your targeted node to your current position");
c->Message(0, "#path process file_name: processes the map file and tries to automatically generate a rudimentary path setup and then dumps the current zone->pathing to a file of your naming.");
c->Message(0, "#path resort [nodes]: resorts the connections/nodes after you've manually altered them so they'll work.");
return;
}
if(!strcasecmp(sep->arg[1], "shownodes"))
{
ShowNodes();
return;
}
if (!strcasecmp(sep->arg[1], "show"))
{
if (c->GetTarget() != nullptr) {
auto target = c->GetTarget();
glm::vec3 end(target->GetX(), target->GetY(), target->GetZ());
glm::vec3 start(c->GetX(), c->GetY(), c->GetZ());
glm::vec3 end(target->GetX(), target->GetY(), target->GetZ());
ShowPath(c, start, end);
}
return;
}
if (!strcasecmp(sep->arg[1], "reload"))
{
Load(m_impl->FileName);
return;
}
if (!strcasecmp(sep->arg[1], "info"))
{
NodeInfo(c);
return;
}
}
void PathfinderWaypoint::Load(const std::string &filename) {
PathFileHeader Head;
Head.PathNodeCount = 0;
Head.version = 2;
FILE *f = fopen(filename.c_str(), "rb");
if (f) {
char Magic[10];
fread(&Magic, 9, 1, f);
if (strncmp(Magic, "EQEMUPATH", 9))
{
Log(Logs::General, Logs::Error, "Bad Magic String in .path file.");
fclose(f);
return;
}
fread(&Head, sizeof(Head), 1, f);
Log(Logs::General, Logs::Status, "Path File Header: Version %ld, PathNodes %ld",
(long)Head.version, (long)Head.PathNodeCount);
if (Head.version == 2)
{
LoadV2(f, Head);
return;
}
else if (Head.version == 3) {
LoadV3(f, Head);
return;
}
else {
Log(Logs::General, Logs::Error, "Unsupported path file version.");
fclose(f);
return;
}
}
}
void PathfinderWaypoint::LoadV2(FILE *f, const PathFileHeader &header)
{
std::unique_ptr<PathNode[]> PathNodes(new PathNode[header.PathNodeCount]);
fread(PathNodes.get(), sizeof(PathNode), header.PathNodeCount, f);
int MaxNodeID = header.PathNodeCount - 1;
m_impl->PathFileValid = true;
m_impl->Nodes.reserve(header.PathNodeCount);
for (uint32 i = 0; i < header.PathNodeCount; ++i)
{
auto &n = PathNodes[i];
Point p = Point(n.v.x, n.v.y, n.v.z);
m_impl->Tree.insert(std::make_pair(p, i));
boost::add_vertex(m_impl->Graph);
Node node;
node.id = i;
node.v = n.v;
node.bestz = n.bestz;
m_impl->Nodes.push_back(node);
}
auto weightmap = boost::get(boost::edge_weight, m_impl->Graph);
for (uint32 i = 0; i < header.PathNodeCount; ++i) {
for (uint32 j = 0; j < 50; ++j)
{
auto &node = m_impl->Nodes[i];
if (PathNodes[i].Neighbours[j].id > MaxNodeID)
{
Log(Logs::General, Logs::Error, "Path Node %i, Neighbour %i (%i) out of range.", i, j, PathNodes[i].Neighbours[j].id);
m_impl->PathFileValid = false;
}
if (PathNodes[i].Neighbours[j].id > 0) {
GraphType::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = boost::add_edge(PathNodes[i].id, PathNodes[i].Neighbours[j].id, m_impl->Graph);
weightmap[e] = PathNodes[i].Neighbours[j].distance;
Edge edge;
edge.distance = PathNodes[i].Neighbours[j].distance;
edge.door_id = PathNodes[i].Neighbours[j].DoorID;
edge.teleport = PathNodes[i].Neighbours[j].Teleport;
m_impl->Edges[std::make_pair(PathNodes[i].id, PathNodes[i].Neighbours[j].id)] = edge;
node.edges[PathNodes[i].Neighbours[j].id] = edge;
}
}
}
BuildGraph();
fclose(f);
}
void PathfinderWaypoint::LoadV3(FILE *f, const PathFileHeader &header)
{
m_impl->Nodes.reserve(header.PathNodeCount);
uint32 edge_count = 0;
fread(&edge_count, sizeof(uint32), 1, f);
for (int i = 0; i < header.PathNodeCount; ++i)
{
uint32 id = 0;
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float best_z = 0.0f;
fread(&id, sizeof(uint32), 1, f);
fread(&x, sizeof(float), 1, f);
fread(&y, sizeof(float), 1, f);
fread(&z, sizeof(float), 1, f);
fread(&best_z, sizeof(float), 1, f);
Node n;
n.id = id;
n.bestz = best_z;
n.v.x = x;
n.v.y = y;
n.v.z = z;
m_impl->Nodes.push_back(n);
}
for (uint32 j = 0; j < edge_count; ++j) {
uint32 from = 0;
uint32 to = 0;
int8 teleport = 0;
float distance = 0.0f;
int32 door_id = 0;
fread(&from, sizeof(uint32), 1, f);
fread(&to, sizeof(uint32), 1, f);
fread(&teleport, sizeof(int8), 1, f);
fread(&distance, sizeof(float), 1, f);
fread(&door_id, sizeof(int32), 1, f);
Edge e;
e.teleport = teleport > 0 ? true : false;
e.distance = distance;
e.door_id = door_id;
auto &n = m_impl->Nodes[from];
n.edges[to] = e;
}
m_impl->PathFileValid = true;
BuildGraph();
fclose(f);
}
@ -327,42 +382,7 @@ void PathfinderWaypoint::ShowNodes()
{
for (size_t i = 0; i < m_impl->Nodes.size(); ++i)
{
auto npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
auto c = i / 1000u;
sprintf(npc_type->name, "Node%u", c);
npc_type->cur_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
npc_type->class_ = 9;
npc_type->deity = 1;
npc_type->level = 75;
npc_type->npc_id = 0;
npc_type->loottable_id = 0;
npc_type->texture = 1;
npc_type->light = 0;
npc_type->runspeed = 0;
npc_type->d_melee_texture1 = 1;
npc_type->d_melee_texture2 = 1;
npc_type->merchanttype = 1;
npc_type->bodytype = 1;
npc_type->STR = 150;
npc_type->STA = 150;
npc_type->DEX = 150;
npc_type->AGI = 150;
npc_type->INT = 150;
npc_type->WIS = 150;
npc_type->CHA = 150;
npc_type->findable = 1;
auto position = glm::vec4(m_impl->Nodes[i].v.x, m_impl->Nodes[i].v.y, m_impl->Nodes[i].v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
ShowNode(m_impl->Nodes[i]);
}
}
@ -385,3 +405,150 @@ void PathfinderWaypoint::ShowPath(Client *c, const glm::vec3 &start, const glm::
c->SendPathPacket(points);
}
void PathfinderWaypoint::NodeInfo(Client *c)
{
if (!c->GetTarget()) {
return;
}
auto node = FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ());
if (node == nullptr) {
return;
}
c->Message(0, "Pathing node: %i at (%.2f, %.2f, %.2f) with bestz %.2f",
node->id, node->v.x, node->v.y, node->v.z, node->bestz);
for (auto &edge : node->edges) {
c->Message(0, "id: %i, distance: %.2f, door id: %i, is teleport: %i",
edge.first,
edge.second.distance,
edge.second.door_id,
edge.second.teleport);
}
}
Node *PathfinderWaypoint::FindPathNodeByCoordinates(float x, float y, float z)
{
for (auto &node : m_impl->Nodes) {
auto dist = Distance(glm::vec3(x, y, z), node.v);
if (dist < 0.1) {
return &node;
}
}
return nullptr;
}
void PathfinderWaypoint::BuildGraph()
{
m_impl->Graph = GraphType();
m_impl->Tree = boost::geometry::index::rtree<RTreeValue, boost::geometry::index::quadratic<16>>();
for (auto &node : m_impl->Nodes) {
Point p = Point(node.v.x, node.v.y, node.v.z);
m_impl->Tree.insert(std::make_pair(p, node.id));
boost::add_vertex(m_impl->Graph);
}
//Populate edges now that we've created all the nodes
auto weightmap = boost::get(boost::edge_weight, m_impl->Graph);
for (auto &node : m_impl->Nodes) {
for (auto &edge : node.edges) {
GraphType::edge_descriptor e;
bool inserted;
boost::tie(e, inserted) = boost::add_edge(node.id, edge.first, m_impl->Graph);
weightmap[e] = edge.second.distance;
}
}
}
std::string DigitToWord(int i)
{
std::string digit = std::to_string(i);
std::string ret;
for (size_t idx = 0; idx < digit.length(); ++idx) {
if (!ret.empty()) {
ret += "_";
}
switch (digit[idx]) {
case '0':
ret += "Zero";
break;
case '1':
ret += "One";
break;
case '2':
ret += "Two";
break;
case '3':
ret += "Three";
break;
case '4':
ret += "Four";
break;
case '5':
ret += "Five";
break;
case '6':
ret += "Six";
break;
case '7':
ret += "Seven";
break;
case '8':
ret += "Eight";
break;
case '9':
ret += "Nine";
break;
default:
break;
}
}
return ret;
}
void PathfinderWaypoint::ShowNode(const Node &n) {
auto npc_type = new NPCType;
memset(npc_type, 0, sizeof(NPCType));
sprintf(npc_type->name, "%s", DigitToWord(n.id).c_str());
sprintf(npc_type->lastname, "%i", n.id);
npc_type->cur_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
npc_type->class_ = 9;
npc_type->deity = 1;
npc_type->level = 75;
npc_type->npc_id = 0;
npc_type->loottable_id = 0;
npc_type->texture = 1;
npc_type->light = 0;
npc_type->runspeed = 0;
npc_type->d_melee_texture1 = 1;
npc_type->d_melee_texture2 = 1;
npc_type->merchanttype = 1;
npc_type->bodytype = 1;
npc_type->show_name = true;
npc_type->STR = 150;
npc_type->STA = 150;
npc_type->DEX = 150;
npc_type->AGI = 150;
npc_type->INT = 150;
npc_type->WIS = 150;
npc_type->CHA = 150;
npc_type->findable = 1;
auto position = glm::vec4(n.v.x, n.v.y, n.v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
}

View File

@ -3,6 +3,7 @@
#include "pathfinder_interface.h"
struct PathFileHeader;
struct Node;
class PathfinderWaypoint : public IPathfinder
{
@ -15,10 +16,15 @@ public:
virtual void DebugCommand(Client *c, const Seperator *sep);
private:
void Load(const std::string &filename);
void LoadV2(FILE *f, const PathFileHeader &header);
void LoadV3(FILE *f, const PathFileHeader &header);
void ShowNodes();
void ShowPath(Client *c, const glm::vec3 &start, const glm::vec3 &end);
void NodeInfo(Client *c);
Node *FindPathNodeByCoordinates(float x, float y, float z);
void BuildGraph();
void ShowNode(const Node &n);
struct Implementation;
std::unique_ptr<Implementation> m_impl;

View File

@ -26,7 +26,12 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa
PathingDestination = To;
WaypointChanged = true;
NodeReached = false;
return (*Route.begin()).pos;
if (Route.empty()) {
return From;
}
else {
return (*Route.begin()).pos;
}
}
else {
if (PathRecalcTimer->Check()) {