From 03571e7a76c67a1c643524bca911ddcc5ab6d92f Mon Sep 17 00:00:00 2001 From: Jeremiah Morgan Date: Tue, 19 Nov 2024 12:45:18 +0000 Subject: [PATCH] Pathfinding: Precomputeportal nodes for A Star --- copying.md | 1 + libopenage/datastructure/pairing_heap.h | 282 +++++++++++------------- libopenage/pathfinding/demo/demo_1.cpp | 1 + libopenage/pathfinding/grid.cpp | 23 ++ libopenage/pathfinding/grid.h | 18 ++ libopenage/pathfinding/pathfinder.cpp | 85 ++++--- libopenage/pathfinding/pathfinder.h | 63 +++++- libopenage/util/file.cpp | 17 ++ libopenage/util/file.h | 3 +- libopenage/util/fslike/directory.cpp | 9 + libopenage/util/fslike/directory.h | 3 +- libopenage/util/path.cpp | 2 +- libopenage/util/path.h | 2 +- 13 files changed, 307 insertions(+), 202 deletions(-) diff --git a/copying.md b/copying.md index ec1ddd2a46..61153fb9cf 100644 --- a/copying.md +++ b/copying.md @@ -156,6 +156,7 @@ _the openage authors_ are: | Nikhil Ghosh | NikhilGhosh75 | nghosh606 à gmail dawt com | | Edvin Lindholm | EdvinLndh | edvinlndh à gmail dawt com | | Jeremiah Morgan | jere8184 | jeremiahmorgan dawt bham à outlook dawt com | +| Tobias Alam | alamt22 | tobiasal à umich dawt edu | If you're a first-time committer, add yourself to the above list. This is not just for legal reasons, but also to keep an overview of all those nicknames. diff --git a/libopenage/datastructure/pairing_heap.h b/libopenage/datastructure/pairing_heap.h index 662a683ee6..d7b2d745ab 100644 --- a/libopenage/datastructure/pairing_heap.h +++ b/libopenage/datastructure/pairing_heap.h @@ -81,13 +81,13 @@ class PairingHeapNode { // it must not have siblings as they will get lost. new_child->prev_sibling = nullptr; - new_child->next_sibling = this->child; + new_child->next_sibling = this->first_child; - if (this->child != nullptr) { - this->child->prev_sibling = new_child; + if (this->first_child != nullptr) { + this->first_child->prev_sibling = new_child; } - this->child = new_child; + this->first_child = new_child; new_child->parent = this; } @@ -156,10 +156,10 @@ class PairingHeapNode { */ void loosen() { // release us from some other node - if (this->parent and this->parent->child == this) { + if (this->parent and this->parent->first_child == this) { // we are child // make the next sibling child - this->parent->child = this->next_sibling; + this->parent->first_child = this->next_sibling; } // if we have a previous sibling if (this->prev_sibling != nullptr) { @@ -179,7 +179,7 @@ class PairingHeapNode { } private: - this_type *child = nullptr; + this_type *first_child = nullptr; this_type *prev_sibling = nullptr; this_type *next_sibling = nullptr; this_type *parent = nullptr; // for decrease-key and delete @@ -230,12 +230,89 @@ class PairingHeap final { } /** - * returns and removes the smallest item on the heap. + * returns the smallest item on the heap and deletes it. + * also known as delete_min. + * _________ + * Ω(log log n), O(2^(2*√log log n')) + * caller must eventually either add node back to heap or delete it */ T pop() { - element_t poped_node = this->pop_node(); - T data = std::move(poped_node->data); - delete poped_node; + if (this->root_node == nullptr) { + throw Error{MSG(err) << "Can't pop an empty heap!"}; + } + + // 0. remove tree root, it's the minimum. + element_t ret = this->root_node; + + if (!this->nodes.erase(ret)) { + throw Error{MSG(err) << "didn't remove node"}; + } + + this->node_count -= 1; + element_t current_sibling = this->root_node->first_child; + this->root_node = nullptr; + + // 1. link root children pairwise, last node may be alone + element_t first_pair = nullptr; + element_t previous_pair = nullptr; + + while (current_sibling != nullptr) [[unlikely]] { + element_t link0 = current_sibling; + element_t link1 = current_sibling->next_sibling; + + // pair link0 and link1 + if (link1 != nullptr) { + // get the first sibling for next pair, just in advance. + current_sibling = link1->next_sibling; + + // do the link: merges two nodes, smaller one = root. + element_t link_root = link0->link_with(link1); + link_root->parent = nullptr; + + if (previous_pair == nullptr) { + // this was the first pair + first_pair = link_root; + first_pair->prev_sibling = nullptr; + } + else { + // store node as next sibling in previous pair + previous_pair->next_sibling = link_root; + link_root->prev_sibling = previous_pair; + } + + previous_pair = link_root; + link_root->next_sibling = nullptr; + } + else { + // link0 is the last and unpaired root child. + link0->parent = nullptr; + if (previous_pair == nullptr) { + // link0 was the only node + first_pair = link0; + link0->prev_sibling = nullptr; + } + else { + previous_pair->next_sibling = link0; + link0->prev_sibling = previous_pair; + } + link0->next_sibling = nullptr; + current_sibling = nullptr; + } + } + + + // 2. then link remaining trees to the last one, from right to left + if (first_pair != nullptr) { + this->root_node = first_pair->link_backwards(); + } + + // (to find those two lines, 14h of debugging passed) + ret->loosen(); + ret->first_child = nullptr; + + // and it's done! + T data = std::move(ret->data); + delete ret; return data; } @@ -283,27 +360,44 @@ class PairingHeap final { * * O(1) (but slower than decrease), and O(pop) when node is the root. */ - void update(const element_t &node) { + void update(element_t &node) { if (node != this->root_node) [[likely]] { - this->unlink_node(node); - this->push_node(node); + node = this->push(this->remove_node(node)); } else { // it's the root node, so we just pop and push it. - this->push_node(this->pop_node()); + node = this->push(this->pop()); } } - /** - * Remove node from tree, return its data and destroy the node. - * O(pop_node) + * remove a node from the heap. Return its data. + * + * If the item is the current root, just pop(). + * else, cut the node from its parent, pop() that subtree + * and merge these trees. + * + * O(pop_node) */ - T remove_node(element_t &node) { - this->unlink_node(node); - T data = std::move(node->data); - delete node; - return data; + T remove_node(const element_t &node) { + if (node == this->root_node) { + return this->pop(); + } + else { + node->loosen(); + + element_t real_root = this->root_node; + this->root_node = node; + T data = this->pop(); + + element_t new_root = this->root_node; + this->root_node = real_root; + + if (new_root != nullptr) { + this->root_insert(new_root); + } + return data; + } } /** @@ -316,9 +410,6 @@ class PairingHeap final { delete node; } this->nodes.clear(); - -#if OPENAGE_PAIRINGHEAP_DEBUG -#endif } /** @@ -378,15 +469,15 @@ class PairingHeap final { } } - if (root->child) { - if (root->child == root->next_sibling) { - throw Error{ERR << "child is next_sibling"}; + if (root->first_child) { + if (root->first_child == root->next_sibling) { + throw Error{ERR << "first_child is next_sibling"}; } - if (root->child == root->prev_sibling) { - throw Error{ERR << "child is prev_sibling"}; + if (root->first_child == root->prev_sibling) { + throw Error{ERR << "first_child is prev_sibling"}; } - if (root->child == root->parent) { - throw Error{ERR << "child is parent"}; + if (root->first_child == root->parent) { + throw Error{ERR << "first_child is parent"}; } } @@ -394,7 +485,7 @@ class PairingHeap final { if (found_nodes.find(root->parent) == std::end(found_nodes)) { throw Error{ERR << "parent node is not known"}; } - element_t child = root->parent->child; + element_t child = root->parent->first_child; element_t lastchild; bool foundvianext = false, foundviaprev = false; @@ -492,43 +583,12 @@ class PairingHeap final { } private: - /** - * Unlink a node from the heap. - * - * If the item is the current root, just pop(). - * else, cut the node from its parent, pop() that subtree - * and merge these trees. - * - * O(pop_node) - * caller must eventually add node back to heap or delete node - */ - void unlink_node(const element_t &node) { - if (node == this->root_node) { - this->pop_node(); - } - else { - node->loosen(); - - element_t real_root = this->root_node; - this->root_node = node; - this->pop_node(); - - element_t new_root = this->root_node; - this->root_node = real_root; - - if (new_root != nullptr) { - this->root_insert(new_root); - } - } - } - - void walk_tree(const element_t &root, const std::function &func) const { func(root); if (root) { - auto node = root->child; + auto node = root->first_child; while (true) { if (not node) { break; @@ -560,96 +620,6 @@ class PairingHeap final { } - /** - * returns the smallest item on the heap and deletes it. - * also known as delete_min. - * _________ - * Ω(log log n), O(2^(2*√log log n')) - * caller must eventually either add node back to heap or delete it - */ - element_t pop_node() { - if (this->root_node == nullptr) { - throw Error{MSG(err) << "Can't pop an empty heap!"}; - } - - // 0. remove tree root, it's the minimum. - element_t ret = this->root_node; - element_t current_sibling = this->root_node->child; - this->root_node = nullptr; - - // 1. link root children pairwise, last node may be alone - element_t first_pair = nullptr; - element_t previous_pair = nullptr; - - while (current_sibling != nullptr) [[unlikely]] { - element_t link0 = current_sibling; - element_t link1 = current_sibling->next_sibling; - - // pair link0 and link1 - if (link1 != nullptr) { - // get the first sibling for next pair, just in advance. - current_sibling = link1->next_sibling; - - // do the link: merges two nodes, smaller one = root. - element_t link_root = link0->link_with(link1); - link_root->parent = nullptr; - - if (previous_pair == nullptr) { - // this was the first pair - first_pair = link_root; - first_pair->prev_sibling = nullptr; - } - else { - // store node as next sibling in previous pair - previous_pair->next_sibling = link_root; - link_root->prev_sibling = previous_pair; - } - - previous_pair = link_root; - link_root->next_sibling = nullptr; - } - else { - // link0 is the last and unpaired root child. - link0->parent = nullptr; - if (previous_pair == nullptr) { - // link0 was the only node - first_pair = link0; - link0->prev_sibling = nullptr; - } - else { - previous_pair->next_sibling = link0; - link0->prev_sibling = previous_pair; - } - link0->next_sibling = nullptr; - current_sibling = nullptr; - } - } - - - // 2. then link remaining trees to the last one, from right to left - if (first_pair != nullptr) { - this->root_node = first_pair->link_backwards(); - } - - this->node_count -= 1; - size_t erase_result = this->nodes.erase(ret); - - -#if OPENAGE_PAIRINGHEAP_DEBUG - if (1 != erase_result) { - throw Error{ERR << "didn't remove node"}; - } -#endif - - // (to find those two lines, 14h of debugging passed) - ret->loosen(); - ret->child = nullptr; - - // and it's done! - return ret; - } - - /** * insert a node into the heap. */ diff --git a/libopenage/pathfinding/demo/demo_1.cpp b/libopenage/pathfinding/demo/demo_1.cpp index cd67e7b79f..1edeba52a1 100644 --- a/libopenage/pathfinding/demo/demo_1.cpp +++ b/libopenage/pathfinding/demo/demo_1.cpp @@ -92,6 +92,7 @@ void path_demo_1(const util::Path &path) { start, target, }; + grid->init_portal_nodes(); timer.start(); Path path_result = pathfinder->get_path(path_request); timer.stop(); diff --git a/libopenage/pathfinding/grid.cpp b/libopenage/pathfinding/grid.cpp index 62a2d3156f..45427b009d 100644 --- a/libopenage/pathfinding/grid.cpp +++ b/libopenage/pathfinding/grid.cpp @@ -98,4 +98,27 @@ void Grid::init_portals() { } } +const nodemap_t &Grid::get_portal_map() { + return portal_nodes; +} + +void Grid::init_portal_nodes() { + // create portal_nodes + for (auto §or : this->sectors) { + for (auto &portal : sector->get_portals()) { + if (!this->portal_nodes.contains(portal->get_id())) { + auto portal_node = std::make_shared(portal); + portal_node->node_sector_0 = sector->get_id(); + portal_node->node_sector_1 = portal_node->portal->get_exit_sector(sector->get_id()); + this->portal_nodes[portal->get_id()] = portal_node; + } + } + } + + // init portal_node exits + for (auto &[id, node] : this->portal_nodes) { + node->init_exits(this->portal_nodes); + } +} + } // namespace openage::path diff --git a/libopenage/pathfinding/grid.h b/libopenage/pathfinding/grid.h index 8f24743433..314d107a2e 100644 --- a/libopenage/pathfinding/grid.h +++ b/libopenage/pathfinding/grid.h @@ -7,6 +7,7 @@ #include #include +#include "pathfinding/pathfinder.h" #include "pathfinding/types.h" #include "util/vector.h" @@ -95,6 +96,16 @@ class Grid { */ void init_portals(); + /** + * returns map of portal ids to portal nodes + */ + const nodemap_t &get_portal_map(); + + /** + * Initialize the portal nodes of the grid with neigbouring nodes and distance costs. + */ + void init_portal_nodes(); + private: /** * ID of the grid. @@ -115,6 +126,13 @@ class Grid { * Sectors of the grid. */ std::vector> sectors; + + /** + * maps portal_ids to portal nodes, which store their neigbouring nodes and associated distance costs + * for pathfinding + */ + + nodemap_t portal_nodes; }; diff --git a/libopenage/pathfinding/pathfinder.cpp b/libopenage/pathfinding/pathfinder.cpp index 288f7875df..e46919e62b 100644 --- a/libopenage/pathfinding/pathfinder.cpp +++ b/libopenage/pathfinding/pathfinder.cpp @@ -4,6 +4,7 @@ #include "coord/chunk.h" #include "coord/phys.h" +#include "error/error.h" #include "pathfinding/cost_field.h" #include "pathfinding/flow_field.h" #include "pathfinding/grid.h" @@ -201,6 +202,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req std::vector> result; auto grid = this->grids.at(request.grid_id); + auto &portal_map = grid->get_portal_map(); auto sector_size = grid->get_sector_size(); auto start_sector_x = request.start.ne / sector_size; @@ -210,8 +212,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req // path node storage, always provides cheapest next node. heap_t node_candidates; - // list of known portals and corresponding node. - nodemap_t visited_portals; + std::unordered_set visited_portals; // TODO: Compute cost to travel from one portal to another when creating portals // const int distance_cost = 1; @@ -223,7 +224,8 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req continue; } - auto portal_node = std::make_shared(portal, start_sector->get_id(), nullptr); + auto &portal_node = portal_map.at(portal->get_id()); + portal_node->entry_sector = start_sector->get_id(); auto sector_pos = grid->get_sector(portal->get_exit_sector(start_sector->get_id()))->get_position().to_tile(sector_size); auto portal_pos = portal->get_exit_center(start_sector->get_id()); @@ -235,7 +237,9 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req portal_node->future_cost = portal_node->current_cost + heuristic_cost; portal_node->heap_node = node_candidates.push(portal_node); - visited_portals[portal->get_id()] = portal_node; + portal_node->prev_portal = nullptr; + portal_node->was_best = false; + visited_portals.insert(portal->get_id()); } // track the closest we can get to the end position @@ -266,20 +270,21 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req } // get the exits of the current node - auto exits = current_node->get_exits(visited_portals, current_node->entry_sector); + const auto &exits = current_node->get_exits(current_node->entry_sector); // evaluate all neighbors of the current candidate for further progress - for (auto &exit : exits) { - if (exit->was_best) { + for (auto &[exit, distance_cost] : exits) { + exit->entry_sector = current_node->portal->get_exit_sector(current_node->entry_sector); + bool not_visited = !visited_portals.contains(exit->portal->get_id()); + + if (not_visited) { + exit->was_best = false; + } + else if (exit->was_best) { continue; } - // Get distance cost (from current node to exit node) - auto distance_cost = Pathfinder::distance_cost( - current_node->portal->get_exit_center(current_node->entry_sector), - exit->portal->get_entry_center(exit->entry_sector)); - bool not_visited = !visited_portals.contains(exit->portal->get_id()); auto tentative_cost = current_node->current_cost + distance_cost; if (not_visited or tentative_cost < exit->current_cost) { @@ -300,7 +305,7 @@ const Pathfinder::portal_star_t Pathfinder::portal_a_star(const PathRequest &req if (not_visited) { exit->heap_node = node_candidates.push(exit); - visited_portals[exit->portal->get_id()] = exit; + visited_portals.insert(exit->portal->get_id()); } else { node_candidates.decrease(exit->heap_node); @@ -463,6 +468,16 @@ int Pathfinder::distance_cost(const coord::tile_delta &portal1_pos, } +PortalNode::PortalNode(const std::shared_ptr &portal) : + portal{portal}, + entry_sector{NULL}, + future_cost{std::numeric_limits::max()}, + current_cost{std::numeric_limits::max()}, + heuristic_cost{std::numeric_limits::max()}, + was_best{false}, + prev_portal{nullptr}, + heap_node{nullptr} {} + PortalNode::PortalNode(const std::shared_ptr &portal, sector_id_t entry_sector, const node_t &prev_portal) : @@ -511,27 +526,37 @@ std::vector PortalNode::generate_backtrace() { return waypoints; } -std::vector PortalNode::get_exits(const nodemap_t &nodes, - sector_id_t entry_sector) { - auto &exits = this->portal->get_exits(entry_sector); - std::vector exit_nodes; - exit_nodes.reserve(exits.size()); +void PortalNode::init_exits(const nodemap_t &node_map) { + auto exits = this->portal->get_exits(this->node_sector_0); + for (auto &exit : exits) { + int distance_cost = Pathfinder::distance_cost( + this->portal->get_exit_center(this->node_sector_0), + exit->get_entry_center(this->node_sector_1)); + + auto exit_node = node_map.at(exit->get_id()); + this->exits_1[exit_node] = distance_cost; + } - auto exit_sector = this->portal->get_exit_sector(entry_sector); + exits = this->portal->get_exits(this->node_sector_1); for (auto &exit : exits) { - auto exit_id = exit->get_id(); + int distance_cost = Pathfinder::distance_cost( + this->portal->get_exit_center(this->node_sector_1), + exit->get_entry_center(this->node_sector_0)); - auto exit_node = nodes.find(exit_id); - if (exit_node != nodes.end()) { - exit_nodes.push_back(exit_node->second); - } - else { - exit_nodes.push_back(std::make_shared(exit, - exit_sector, - this->shared_from_this())); - } + auto exit_node = node_map.at(exit->get_id()); + this->exits_0[exit_node] = distance_cost; + } +} + +const PortalNode::exits_t &PortalNode::get_exits(sector_id_t entry_sector) { + ENSURE(entry_sector == this->node_sector_0 || entry_sector == this->node_sector_1, "Invalid entry sector"); + + if (this->node_sector_0 == entry_sector) { + return exits_1; + } + else { + return exits_0; } - return exit_nodes; } diff --git a/libopenage/pathfinding/pathfinder.h b/libopenage/pathfinding/pathfinder.h index 1809b7524d..e2529987b7 100644 --- a/libopenage/pathfinding/pathfinder.h +++ b/libopenage/pathfinding/pathfinder.h @@ -2,9 +2,9 @@ #pragma once +#include #include #include -#include #include "coord/tile.h" #include "datastructure/pairing_heap.h" @@ -62,6 +62,17 @@ class Pathfinder { */ const Path get_path(const PathRequest &request); + + /** + * Calculate the distance cost between two portals. + * + * @param portal1_pos Center of the first portal (relative to sector origin). + * @param portal2_pos Center of the second portal (relative to sector origin). + * + * @return Distance cost between the portal centers. + */ + static int distance_cost(const coord::tile_delta &portal1_pos, const coord::tile_delta &portal2_pos); + private: using portal_star_t = std::pair>>; @@ -100,15 +111,6 @@ class Pathfinder { */ static int heuristic_cost(const coord::tile &portal_pos, const coord::tile &target_pos); - /** - * Calculate the distance cost between two portals. - * - * @param portal1_pos Center of the first portal (relative to sector origin). - * @param portal2_pos Center of the second portal (relative to sector origin). - * - * @return Distance cost between the portal centers. - */ - static int distance_cost(const coord::tile_delta &portal1_pos, const coord::tile_delta &portal2_pos); /** * Grids managed by this pathfinder. @@ -147,6 +149,7 @@ using nodemap_t = std::unordered_map; */ class PortalNode : public std::enable_shared_from_this { public: + PortalNode(const std::shared_ptr &portal); PortalNode(const std::shared_ptr &portal, sector_id_t entry_sector, const node_t &prev_portal); @@ -178,9 +181,25 @@ class PortalNode : public std::enable_shared_from_this { std::vector generate_backtrace(); /** - * Get all exits of a node. + * init PortalNode::exits. + */ + void init_exits(const nodemap_t &node_map); + + + /** + * maps node_t of a neigbhour portal to the distance cost to travel between the portals */ - std::vector get_exits(const nodemap_t &nodes, sector_id_t entry_sector); + using exits_t = std::map; + + + /** + * Get the exit portals reachable via the portal when entering from a specified sector. + * + * @param entry_sector Sector from which the portal is entered. + * + * @return Exit portals nodes reachable from the portal. + */ + const exits_t &get_exits(sector_id_t entry_sector); /** * The portal this node is associated to. @@ -226,6 +245,26 @@ class PortalNode : public std::enable_shared_from_this { * Priority queue node that contains this path node. */ heap_t::element_t heap_node; + + /** + * First sector connected by the portal. + */ + sector_id_t node_sector_0; + + /** + * Second sector connected by the portal. + */ + sector_id_t node_sector_1; + + /** + * Exits in sector 0 reachable from the portal. + */ + exits_t exits_0; + + /** + * Exits in sector 1 reachable from the portal. + */ + exits_t exits_1; }; diff --git a/libopenage/util/file.cpp b/libopenage/util/file.cpp index 5aa630d4cc..d035ee34f2 100644 --- a/libopenage/util/file.cpp +++ b/libopenage/util/file.cpp @@ -14,6 +14,7 @@ #include "util/filelike/native.h" #include "util/filelike/python.h" +#include "util/fslike/directory.h" #include "util/path.h" #include "util/strings.h" @@ -121,5 +122,21 @@ std::ostream &operator<<(std::ostream &stream, const File &file) { return stream; } +File File::get_temp_file(bool executable) { + fslike::Directory temp_dir = fslike::Directory::get_temp_directory(); + std::string file_name = std::tmpnam(nullptr); + std::ostringstream dir_path; + temp_dir.repr(dir_path); + + if (executable) { + // 0755 == rwxr-xr-x + File file_wrapper = File(dir_path.str() + file_name, 0755); + return file_wrapper; + } + + // 0644 == rw-r--r-- + File file_wrapper = File(dir_path.str() + file_name, 0644); + return file_wrapper; +} } // namespace openage::util diff --git a/libopenage/util/file.h b/libopenage/util/file.h index 6724b5f795..e2c9a8a992 100644 --- a/libopenage/util/file.h +++ b/libopenage/util/file.h @@ -98,9 +98,10 @@ class OAAPI File { void flush(); ssize_t size(); std::vector get_lines(); - std::shared_ptr get_fileobj() const; + static File get_temp_file(bool executable = false); + protected: std::shared_ptr filelike; diff --git a/libopenage/util/fslike/directory.cpp b/libopenage/util/fslike/directory.cpp index 420757f3a6..847ffe4d80 100644 --- a/libopenage/util/fslike/directory.cpp +++ b/libopenage/util/fslike/directory.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -292,4 +293,12 @@ std::ostream &Directory::repr(std::ostream &stream) { return stream; } +Directory Directory::get_temp_directory() { + std::filesystem::path path = std::filesystem::temp_directory_path() / std::tmpnam(nullptr); + std::string temp_dir_path = path.string(); + bool create = true; + Directory directory = Directory(temp_dir_path, create); + return directory; +} + } // namespace openage::util::fslike diff --git a/libopenage/util/fslike/directory.h b/libopenage/util/fslike/directory.h index 2872b9554b..9f72d49766 100644 --- a/libopenage/util/fslike/directory.h +++ b/libopenage/util/fslike/directory.h @@ -48,6 +48,8 @@ class Directory : public FSLike { std::ostream &repr(std::ostream &) override; + static Directory get_temp_directory(); + protected: /** * resolve the path to an actually usable one. @@ -59,7 +61,6 @@ class Directory : public FSLike { std::string basepath; }; - } // namespace fslike } // namespace util } // namespace openage diff --git a/libopenage/util/path.cpp b/libopenage/util/path.cpp index ff23253533..e521a4ed9e 100644 --- a/libopenage/util/path.cpp +++ b/libopenage/util/path.cpp @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #include "path.h" diff --git a/libopenage/util/path.h b/libopenage/util/path.h index d16e1734c6..5d4e079812 100644 --- a/libopenage/util/path.h +++ b/libopenage/util/path.h @@ -1,4 +1,4 @@ -// Copyright 2015-2023 the openage authors. See copying.md for legal info. +// Copyright 2015-2024 the openage authors. See copying.md for legal info. #pragma once