diff --git a/libopenage/gamestate/component/internal/ownership.cpp b/libopenage/gamestate/component/internal/ownership.cpp index 5fcfd3c007f..d4d5151e5de 100644 --- a/libopenage/gamestate/component/internal/ownership.cpp +++ b/libopenage/gamestate/component/internal/ownership.cpp @@ -8,7 +8,7 @@ namespace openage::gamestate::component { Ownership::Ownership(const std::shared_ptr &loop, - const ownership_id_t owner_id, + const player_id_t owner_id, const time::time_t &creation_time) : owner(loop, 0) { this->owner.set_last(creation_time, owner_id); @@ -22,11 +22,11 @@ inline component_t Ownership::get_type() const { return component_t::OWNERSHIP; } -void Ownership::set_owner(const time::time_t &time, const ownership_id_t owner_id) { +void Ownership::set_owner(const time::time_t &time, const player_id_t owner_id) { this->owner.set_last(time, owner_id); } -const curve::Discrete &Ownership::get_owners() const { +const curve::Discrete &Ownership::get_owners() const { return this->owner; } diff --git a/libopenage/gamestate/component/internal/ownership.h b/libopenage/gamestate/component/internal/ownership.h index b28448a557e..cc0f2752499 100644 --- a/libopenage/gamestate/component/internal/ownership.h +++ b/libopenage/gamestate/component/internal/ownership.h @@ -8,6 +8,7 @@ #include "curve/discrete.h" #include "gamestate/component/internal_component.h" #include "gamestate/component/types.h" +#include "gamestate/types.h" #include "time/time.h" @@ -18,8 +19,6 @@ class EventLoop; namespace gamestate::component { -using ownership_id_t = uint64_t; - class Ownership : public InternalComponent { public: /** @@ -30,7 +29,7 @@ class Ownership : public InternalComponent { * @param creation_time Ingame creation time of the component. */ Ownership(const std::shared_ptr &loop, - const ownership_id_t owner_id, + const player_id_t owner_id, const time::time_t &creation_time); /** @@ -48,20 +47,20 @@ class Ownership : public InternalComponent { * @param time Time at which the owner ID is set. * @param owner_id New owner ID. */ - void set_owner(const time::time_t &time, const ownership_id_t owner_id); + void set_owner(const time::time_t &time, const player_id_t owner_id); /** * Get the owner IDs over time. * * @return Owner ID curve. */ - const curve::Discrete &get_owners() const; + const curve::Discrete &get_owners() const; private: /** * Owner ID storage over time. */ - curve::Discrete owner; + curve::Discrete owner; }; } // namespace gamestate::component diff --git a/libopenage/gamestate/entity_factory.cpp b/libopenage/gamestate/entity_factory.cpp index f6aa7f4fc96..f6c3d6f6608 100644 --- a/libopenage/gamestate/entity_factory.cpp +++ b/libopenage/gamestate/entity_factory.cpp @@ -31,6 +31,7 @@ #include "gamestate/game_entity.h" #include "gamestate/game_state.h" #include "gamestate/manager.h" +#include "gamestate/player.h" #include "gamestate/system/types.h" #include "log/message.h" #include "renderer/render_factory.h" @@ -152,17 +153,22 @@ std::shared_ptr create_test_activity() { } EntityFactory::EntityFactory() : - next_id{0}, + next_entity_id{0}, + next_player_id{0}, render_factory{nullptr} { } std::shared_ptr EntityFactory::add_game_entity(const std::shared_ptr &loop, const std::shared_ptr &state, + player_id_t owner_id, const nyan::fqon_t &nyan_entity) { - auto entity = std::make_shared(this->get_next_id()); + auto entity = std::make_shared(this->get_next_entity_id()); entity->set_manager(std::make_shared(loop, state, entity)); - init_components(loop, state, entity, nyan_entity); + // use the owner's data to initialize the entity + // this ensures that only the owner's tech upgrades apply + auto db_view = state->get_player(owner_id)->get_db_view(); + init_components(loop, db_view, entity, nyan_entity); if (this->render_factory) { entity->set_render_entity(this->render_factory->add_world_render_entity()); @@ -171,6 +177,17 @@ std::shared_ptr EntityFactory::add_game_entity(const std::shared_ptr return entity; } +std::shared_ptr EntityFactory::add_player(const std::shared_ptr & /* loop */, + const std::shared_ptr &state, + const nyan::fqon_t & /* player_setup */) { + auto player = std::make_shared(this->get_next_player_id(), + state->get_db_view()->new_child()); + + // TODO: Components (for resources, diplomacy, etc.) + + return player; +} + void EntityFactory::attach_renderer(const std::shared_ptr &render_factory) { std::unique_lock lock{this->mutex}; @@ -178,7 +195,7 @@ void EntityFactory::attach_renderer(const std::shared_ptr &loop, - const std::shared_ptr &state, + const std::shared_ptr &owner_db_view, const std::shared_ptr &entity, const nyan::fqon_t &nyan_entity) { auto position = std::make_shared(loop); @@ -190,13 +207,12 @@ void EntityFactory::init_components(const std::shared_ptr(loop); entity->add_component(command_queue); - auto db_view = state->get_nyan_db(); - auto nyan_obj = db_view->get_object(nyan_entity); + auto nyan_obj = owner_db_view->get_object(nyan_entity); nyan::set_t abilities = nyan_obj.get_set("GameEntity.abilities"); for (const auto &ability_val : abilities) { auto ability_fqon = std::dynamic_pointer_cast(ability_val.get_ptr())->get_name(); - auto ability_obj = db_view->get_object(ability_fqon); + auto ability_obj = owner_db_view->get_object(ability_fqon); auto ability_parent = ability_obj.get_parents()[0]; if (ability_parent == "engine.ability.type.Move") { @@ -218,7 +234,7 @@ void EntityFactory::init_components(const std::shared_ptr(setting.get_ptr()); - auto setting_obj = db_view->get_object(setting_obj_val->get_name()); + auto setting_obj = owner_db_view->get_object(setting_obj_val->get_name()); auto attribute = setting_obj.get_object("AttributeSetting.attribute"); auto start_value = setting_obj.get_int("AttributeSetting.starting_value"); @@ -238,9 +254,16 @@ void EntityFactory::init_components(const std::shared_ptradd_component(activity); } -entity_id_t EntityFactory::get_next_id() { - auto new_id = this->next_id; - this->next_id++; +entity_id_t EntityFactory::get_next_entity_id() { + auto new_id = this->next_entity_id; + this->next_entity_id++; + + return new_id; +} + +player_id_t EntityFactory::get_next_player_id() { + auto new_id = this->next_player_id; + this->next_player_id++; return new_id; } diff --git a/libopenage/gamestate/entity_factory.h b/libopenage/gamestate/entity_factory.h index 3960a08e530..5a369d995ea 100644 --- a/libopenage/gamestate/entity_factory.h +++ b/libopenage/gamestate/entity_factory.h @@ -23,6 +23,7 @@ class RenderFactory; namespace gamestate { class GameEntity; class GameState; +class Player; /** * Creates game entities that contain data of objects inside the game world. @@ -43,14 +44,32 @@ class EntityFactory { * * @param loop Event loop for the gamestate. * @param state State of the game. + * @param owner_id ID of the player owning the entity. * @param nyan_entity fqon of the GameEntity data in the nyan database. * * @return New game entity. */ std::shared_ptr add_game_entity(const std::shared_ptr &loop, const std::shared_ptr &state, + player_id_t owner_id, const nyan::fqon_t &nyan_entity); + /** + * Create a new player. + * + * This just creates the player. The caller is responsible for initializing + * its components and placing it into the game. + * + * @param loop Event loop for the gamestate. + * @param state State of the game. + * @param player_setup fqon of the player setup in the nyan database. + * + * @return New player. + */ + std::shared_ptr add_player(const std::shared_ptr &loop, + const std::shared_ptr &state, + const nyan::fqon_t &player_setup); + /** * Attach a renderer which enables graphical display options for all ingame entities. * @@ -64,12 +83,12 @@ class EntityFactory { * Initialize components of a game entity. * * @param loop Event loop for the gamestate. - * @param state State of the game. + * @param owner_db_view View of the nyan database of the player owning the entity. * @param entity Game entity. * @param nyan_entity fqon of the GameEntity data in the nyan database. */ void init_components(const std::shared_ptr &loop, - const std::shared_ptr &state, + const std::shared_ptr &owner_db_view, const std::shared_ptr &entity, const nyan::fqon_t &nyan_entity); @@ -78,12 +97,24 @@ class EntityFactory { * * @return Unique ID for a game entity. */ - entity_id_t get_next_id(); + entity_id_t get_next_entity_id(); + + /** + * Get a unique ID for creating a player. + * + * @return Unique ID for a player. + */ + player_id_t get_next_player_id(); /** * ID of the next game entity to be created. */ - entity_id_t next_id; + entity_id_t next_entity_id; + + /** + * ID of the next player to be created. + */ + player_id_t next_player_id; /** * Factory for creating connector objects to the renderer which make game entities displayable. diff --git a/libopenage/gamestate/event/spawn_entity.cpp b/libopenage/gamestate/event/spawn_entity.cpp index d618bcd2085..a2274234501 100644 --- a/libopenage/gamestate/event/spawn_entity.cpp +++ b/libopenage/gamestate/event/spawn_entity.cpp @@ -101,7 +101,7 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, const param_map ¶ms) { auto gstate = std::dynamic_pointer_cast(state); - auto nyan_db = gstate->get_nyan_db(); + auto nyan_db = gstate->get_db_view(); auto game_entities = nyan_db->get_obj_children_all("engine.util.game_entity.GameEntity"); @@ -151,7 +151,8 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, } // Create entity - auto entity = this->factory->add_game_entity(this->loop, gstate, nyan_entity); + player_id_t owner_id = params.get("owner", 0); + auto entity = this->factory->add_game_entity(this->loop, gstate, owner_id, nyan_entity); // Setup components auto entity_pos = std::dynamic_pointer_cast( @@ -163,7 +164,7 @@ void SpawnEntityHandler::invoke(openage::event::EventLoop & /* loop */, auto entity_owner = std::dynamic_pointer_cast( entity->get_component(component::component_t::OWNERSHIP)); - entity_owner->set_owner(time, params.get("owner", 0)); + entity_owner->set_owner(time, owner_id); auto activity = std::dynamic_pointer_cast( entity->get_component(component::component_t::ACTIVITY)); diff --git a/libopenage/gamestate/game.cpp b/libopenage/gamestate/game.cpp index b496ae7e1e7..90dfe5ede3d 100644 --- a/libopenage/gamestate/game.cpp +++ b/libopenage/gamestate/game.cpp @@ -11,6 +11,7 @@ #include "assets/mod_manager.h" #include "assets/modpack.h" +#include "gamestate/entity_factory.h" #include "gamestate/game_state.h" #include "gamestate/universe.h" #include "util/path.h" @@ -20,12 +21,19 @@ namespace openage::gamestate { Game::Game(const std::shared_ptr &event_loop, - const std::shared_ptr &mod_manager) : + const std::shared_ptr &mod_manager, + const std::shared_ptr &entity_factory) : db{nyan::Database::create()}, state{std::make_shared(this->db, event_loop)}, universe{std::make_shared(state)} { this->load_data(mod_manager); + // TODO: Testing player creation + auto player1 = entity_factory->add_player(event_loop, state, ""); + auto player2 = entity_factory->add_player(event_loop, state, ""); + state->add_player(player1); + state->add_player(player2); + // TODO: This lets the spawner event check which modpacks are loaded, // so that it can decide which entities it can spawn. // This can be removed when we spawn based on game logic rather than diff --git a/libopenage/gamestate/game.h b/libopenage/gamestate/game.h index 9c9eb1def42..aecfd3bd72e 100644 --- a/libopenage/gamestate/game.h +++ b/libopenage/gamestate/game.h @@ -29,6 +29,7 @@ class Path; namespace gamestate { class GameState; +class EntityFactory; class Universe; /** @@ -46,12 +47,13 @@ class Game { /** * Create a new game. * - * @param root_dir openage root directory. * @param event_loop Event simulation loop for the gamestate. * @param mod_manager Mod manager. + * @param entity_factory Factory for creating entities. Used for creating the players. */ Game(const std::shared_ptr &event_loop, - const std::shared_ptr &mod_manager); + const std::shared_ptr &mod_manager, + const std::shared_ptr &entity_factory); ~Game() = default; /** diff --git a/libopenage/gamestate/game_state.cpp b/libopenage/gamestate/game_state.cpp index 759b6388408..9cda3633791 100644 --- a/libopenage/gamestate/game_state.cpp +++ b/libopenage/gamestate/game_state.cpp @@ -8,6 +8,7 @@ #include "log/log.h" #include "gamestate/game_entity.h" +#include "gamestate/player.h" namespace openage::gamestate { @@ -18,7 +19,7 @@ GameState::GameState(const std::shared_ptr &db, db_view{db->new_view()} { } -const std::shared_ptr &GameState::get_nyan_db() { +const std::shared_ptr &GameState::get_db_view() { return this->db_view; } @@ -29,6 +30,13 @@ void GameState::add_game_entity(const std::shared_ptr &entity) { this->game_entities[entity->get_id()] = entity; } +void GameState::add_player(const std::shared_ptr &player) { + if (this->players.contains(player->get_id())) [[unlikely]] { + throw Error(MSG(err) << "Player with ID " << player->get_id() << " already exists"); + } + this->players[player->get_id()] = player; +} + const std::shared_ptr &GameState::get_game_entity(entity_id_t id) const { if (!this->game_entities.contains(id)) [[unlikely]] { throw Error(MSG(err) << "Game entity with ID " << id << " does not exist"); @@ -40,6 +48,13 @@ const std::unordered_map> &GameState::g return this->game_entities; } +const std::shared_ptr &GameState::get_player(player_id_t id) const { + if (!this->players.contains(id)) [[unlikely]] { + throw Error(MSG(err) << "Player with ID " << id << " does not exist"); + } + return this->players.at(id); +} + const std::shared_ptr &GameState::get_mod_manager() const { return this->mod_manager; } diff --git a/libopenage/gamestate/game_state.h b/libopenage/gamestate/game_state.h index c091303548c..d252acaadcb 100644 --- a/libopenage/gamestate/game_state.h +++ b/libopenage/gamestate/game_state.h @@ -26,6 +26,7 @@ class EventLoop; namespace gamestate { class GameEntity; +class Player; /** * State of the game. @@ -45,11 +46,13 @@ class GameState : public openage::event::State { const std::shared_ptr &event_loop); /** - * Get the nyan database view of this state. - * - * @return nyan database view. - */ - const std::shared_ptr &get_nyan_db(); + * Get the nyan database view for the whole game. + * + * Players have individual views for their own data. + * + * @return nyan database view. + */ + const std::shared_ptr &get_db_view(); /** * Add a new game entity to the index. @@ -58,10 +61,18 @@ class GameState : public openage::event::State { */ void add_game_entity(const std::shared_ptr &entity); + /** + * Add a new player to the index. + * + * @param player New player. + */ + void add_player(const std::shared_ptr &player); + /** * Get a game entity by its ID. * * @param id ID of the game entity. + * * @return Game entity with the given ID. */ const std::shared_ptr &get_game_entity(entity_id_t id) const; @@ -74,8 +85,17 @@ class GameState : public openage::event::State { const std::unordered_map> &get_game_entities() const; /** - * TODO: Only for testing. - */ + * Get a player by its ID. + * + * @param id ID of the player. + * + * @return Player with the given ID. + */ + const std::shared_ptr &get_player(player_id_t id) const; + + /** + * TODO: Only for testing. + */ const std::shared_ptr &get_mod_manager() const; void set_mod_manager(const std::shared_ptr &mod_manager); @@ -90,6 +110,11 @@ class GameState : public openage::event::State { */ std::unordered_map> game_entities; + /** + * Map of all players in the current game by their ID. + */ + std::unordered_map> players; + /** * TODO: Only for testing */ diff --git a/libopenage/gamestate/player.cpp b/libopenage/gamestate/player.cpp index ab8d0d3d528..e57c4d8a350 100644 --- a/libopenage/gamestate/player.cpp +++ b/libopenage/gamestate/player.cpp @@ -1,8 +1,24 @@ -// Copyright 2018-2021 the openage authors. See copying.md for legal info. +// Copyright 2018-2023 the openage authors. See copying.md for legal info. #include "player.h" +#include "nyan/nyan.h" + + namespace openage::gamestate { +Player::Player(player_id_t id, + const std::shared_ptr &db_view) : + id{id}, + db_view{db_view} { +} + +player_id_t Player::get_id() const { + return this->id; +} + +const std::shared_ptr &Player::get_db_view() const { + return this->db_view; +} -} // openage::gamestate +} // namespace openage::gamestate diff --git a/libopenage/gamestate/player.h b/libopenage/gamestate/player.h index b5ad5d0bcd7..db6c53b1283 100644 --- a/libopenage/gamestate/player.h +++ b/libopenage/gamestate/player.h @@ -2,6 +2,14 @@ #pragma once +#include + +#include "gamestate/types.h" + + +namespace nyan { +class View; +} // namespace nyan namespace openage::gamestate { @@ -10,8 +18,48 @@ namespace openage::gamestate { */ class Player { public: - Player() = default; + /** + * Create a new player with the given id. + * + * @param id Unique player ID. + * @param db_view View of the nyan database used for the player. + */ + Player(player_id_t id, + const std::shared_ptr &db_view); + + // players can't be copied to prevent duplicate IDs + Player(const Player &) = delete; + Player(Player &&) = default; + + Player &operator=(const Player &) = delete; + Player &operator=(Player &&) = default; + ~Player() = default; + + /** + * Get the unique ID of the player. + * + * @return Unique player ID. + */ + player_id_t get_id() const; + + /** + * Get the nyan game database view for the player. + * + * @return nyan database view. + */ + const std::shared_ptr &get_db_view() const; + +private: + /** + * Player ID. Must be unique. + */ + const player_id_t id; + + /** + * Player view of the nyan game data database. + */ + std::shared_ptr db_view; }; } // namespace openage::gamestate diff --git a/libopenage/gamestate/simulation.cpp b/libopenage/gamestate/simulation.cpp index 1c045548bce..c925888815a 100644 --- a/libopenage/gamestate/simulation.cpp +++ b/libopenage/gamestate/simulation.cpp @@ -54,7 +54,9 @@ void GameSimulation::start() { this->init_event_handlers(); - this->game = std::make_shared(event_loop, this->mod_manager); + this->game = std::make_shared(event_loop, + this->mod_manager, + this->entity_factory); this->running = true; diff --git a/libopenage/gamestate/types.h b/libopenage/gamestate/types.h index 7b90547de62..ad7423853b8 100644 --- a/libopenage/gamestate/types.h +++ b/libopenage/gamestate/types.h @@ -11,4 +11,9 @@ namespace openage::gamestate { */ using entity_id_t = uint64_t; +/** + * Player IDs. + */ +using player_id_t = uint64_t; + } // namespace openage::gamestate