feat: refactored how gameroot handles spawning players
This commit is contained in:
		
							parent
							
								
									49562071c0
								
							
						
					
					
						commit
						78c94e52f2
					
				
							
								
								
									
										140
									
								
								game_root.cpp
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								game_root.cpp
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,19 +1,19 @@
 | 
			
		|||
#include "game_root.hpp"
 | 
			
		||||
#include "game_mode.hpp"
 | 
			
		||||
#include "godot_macros.h"
 | 
			
		||||
#include "level.hpp"
 | 
			
		||||
#include "player.hpp"
 | 
			
		||||
#include "player_input.hpp"
 | 
			
		||||
#include "spawn_point.hpp"
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <godot_cpp/classes/global_constants.hpp>
 | 
			
		||||
#include <godot_cpp/classes/input.hpp>
 | 
			
		||||
#include <godot_cpp/classes/packed_scene.hpp>
 | 
			
		||||
#include <godot_cpp/classes/scene_state.hpp>
 | 
			
		||||
#include <godot_cpp/core/class_db.hpp>
 | 
			
		||||
#include <godot_cpp/templates/pair.hpp>
 | 
			
		||||
#include <godot_cpp/variant/string_name.hpp>
 | 
			
		||||
#include <godot_cpp/variant/utility_functions.hpp>
 | 
			
		||||
#include <godot_cpp/classes/input.hpp>
 | 
			
		||||
#include <godot_cpp/core/class_db.hpp>
 | 
			
		||||
#include "godot_cpp/templates/pair.hpp"
 | 
			
		||||
#include "utils/godot_macros.h"
 | 
			
		||||
#include "utils/player_input.hpp"
 | 
			
		||||
#include "utils/spawn_point.hpp"
 | 
			
		||||
#include "utils/player.hpp"
 | 
			
		||||
#include "game_mode.hpp"
 | 
			
		||||
#include "level.hpp"
 | 
			
		||||
 | 
			
		||||
namespace godot {
 | 
			
		||||
void GameRoot::_bind_methods() {
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +33,9 @@ bool GameRoot::has_singleton() {
 | 
			
		|||
 | 
			
		||||
void GameRoot::_enter_tree() { GDGAMEONLY();
 | 
			
		||||
    // TODO: Replace this with detecting input devices
 | 
			
		||||
    if(this->players.is_empty())
 | 
			
		||||
    if(this->players.is_empty()) {
 | 
			
		||||
        this->player_connected();
 | 
			
		||||
    }
 | 
			
		||||
    this->grab_singleton();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,18 +43,6 @@ void GameRoot::_exit_tree() { GDGAMEONLY();
 | 
			
		|||
    this->release_singleton();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot::set_game_mode(Ref<GameMode> mode) {
 | 
			
		||||
    if(mode.is_null() || !mode.is_valid()) {
 | 
			
		||||
        this->game_mode = Ref<GameMode>();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    this->game_mode = mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<GameMode> GameRoot::get_game_mode() const {
 | 
			
		||||
    return this->game_mode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot::player_connected() {
 | 
			
		||||
    PlayerInput *input = memnew(PlayerInput);
 | 
			
		||||
    this->add_child(input);
 | 
			
		||||
| 
						 | 
				
			
			@ -61,26 +50,17 @@ void GameRoot::player_connected() {
 | 
			
		|||
    this->emit_signal(StringName("player_connected"), input);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GameRoot::initialize_player(IPlayer *player) {
 | 
			
		||||
    KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> *found{nullptr};
 | 
			
		||||
    // find an unassigned player input instance
 | 
			
		||||
    for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> &pair : this->players) {
 | 
			
		||||
        if(pair.value.second == nullptr) {
 | 
			
		||||
            found = &pair;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // no player slots available, notify caller
 | 
			
		||||
    if(!found)
 | 
			
		||||
        return false;
 | 
			
		||||
    player->player_id = found->key;
 | 
			
		||||
    found->value.second = player;
 | 
			
		||||
    player->setup_player_input(found->value.first);
 | 
			
		||||
bool GameRoot::initialize_player(IPlayer *player, uint32_t id) {
 | 
			
		||||
    Pair<PlayerInput*, IPlayer*> &found{this->players.get(id)};
 | 
			
		||||
    this->add_child(player->to_node());
 | 
			
		||||
    player->player_id = id;
 | 
			
		||||
    found.second = player;
 | 
			
		||||
    player->setup_player_input(found.first);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot::reset_game_mode() {
 | 
			
		||||
    this->game_mode.unref();
 | 
			
		||||
    this->set_game_mode(Ref<GameMode>());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot::grab_singleton() {
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +80,48 @@ void GameRoot::release_singleton() {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t GameRoot::find_empty_player_slot() const {
 | 
			
		||||
    for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> const &kvp : this->players) {
 | 
			
		||||
        if(kvp.value.second == nullptr) {
 | 
			
		||||
            return kvp.key;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot::set_game_mode(Ref<GameMode> prototype) {
 | 
			
		||||
    // free all player instances in use
 | 
			
		||||
    for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> &pair : this->players) {
 | 
			
		||||
        if(pair.value.second == nullptr) continue;
 | 
			
		||||
        Node *node = dynamic_cast<Node*>(pair.value.second);
 | 
			
		||||
        if(node == nullptr) {
 | 
			
		||||
            UtilityFunctions::push_error("Attempt to cast player '", pair.key, "' to node failed");
 | 
			
		||||
        } else {
 | 
			
		||||
            node->queue_free();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if(prototype.is_null() || !prototype.is_valid()) {
 | 
			
		||||
        this->game_mode.unref();
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    // create new gamemode instance
 | 
			
		||||
    this->game_mode = prototype->duplicate(false);
 | 
			
		||||
    // copy the game state from the prototype
 | 
			
		||||
    this->game_mode->set_game_state(prototype->get_game_state()->duplicate(false));
 | 
			
		||||
    uint32_t new_player_id = 0;
 | 
			
		||||
    do {
 | 
			
		||||
        new_player_id = this->find_empty_player_slot();
 | 
			
		||||
        IPlayer *player = this->spawn_player(new_player_id);
 | 
			
		||||
        if(player != nullptr)
 | 
			
		||||
            this->initialize_player(player, new_player_id);
 | 
			
		||||
    } while(new_player_id != 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPlayer *GameRoot::spawn_player(uint32_t id) {
 | 
			
		||||
    UtilityFunctions::push_error("GameRoot::spawn_player not implemented");
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
GameRoot *GameRoot::singleton_instance{nullptr};
 | 
			
		||||
 | 
			
		||||
#undef CLASSNAME
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +154,7 @@ Level3D *GameRoot3D::load_level_at(Ref<PackedScene> level, Transform3D at) {
 | 
			
		|||
    this->levels.insert(level->get_path(), instance);
 | 
			
		||||
    // if this is the first level containing a game mode currently active use it's gamemode as a prototype
 | 
			
		||||
    if(this->game_mode.is_null()) {
 | 
			
		||||
        this->change_game_mode(instance->get_game_mode_prototype());
 | 
			
		||||
        this->set_game_mode(instance->get_game_mode_prototype());
 | 
			
		||||
        instance->connect("tree_exited", Callable(this, "reset_game_mode"));
 | 
			
		||||
    }
 | 
			
		||||
    return instance;
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +194,25 @@ Ref<PackedScene> GameRoot3D::get_first_boot_level() const {
 | 
			
		|||
    return this->first_boot_level;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
IPlayer *GameRoot3D::spawn_player(uint32_t id) {
 | 
			
		||||
    if(id == 0) {
 | 
			
		||||
        UtilityFunctions::push_error("Failed to find any valid player slot when spawning player");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    Node *player_node = this->game_mode->get_player_scene()->instantiate();
 | 
			
		||||
    if(player_node == nullptr) {
 | 
			
		||||
        UtilityFunctions::push_error("Failed to instantiate player scene '", this->game_mode->get_player_scene()->get_path(), "'");
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    IPlayer *player = dynamic_cast<IPlayer*>(player_node);
 | 
			
		||||
    if(player == nullptr) {
 | 
			
		||||
        UtilityFunctions::push_error("Player scene does not implement required IPlayer interface");
 | 
			
		||||
        player_node->queue_free();
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    return player;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GameRoot3D::is_valid_level(Ref<PackedScene> &level) {
 | 
			
		||||
    if(level.is_null() || !level.is_valid() || !level->can_instantiate()) {
 | 
			
		||||
        UtilityFunctions::push_error("Can't load level from invalid packed scene");
 | 
			
		||||
| 
						 | 
				
			
			@ -184,25 +225,4 @@ bool GameRoot3D::is_valid_level(Ref<PackedScene> &level) {
 | 
			
		|||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameRoot3D::change_game_mode(Ref<GameMode> prototype) {
 | 
			
		||||
    // free all player instances in use
 | 
			
		||||
    for(KeyValue<uint32_t, Pair<PlayerInput*, IPlayer*>> &pair : this->players) {
 | 
			
		||||
        if(pair.value.second == nullptr) continue;
 | 
			
		||||
        Node *node = dynamic_cast<Node*>(pair.value.second);
 | 
			
		||||
        if(node == nullptr) {
 | 
			
		||||
            UtilityFunctions::push_error("Attempt to cast player '", pair.key, "' to node failed");
 | 
			
		||||
        } else {
 | 
			
		||||
            node->queue_free();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    // create new gamemode instance
 | 
			
		||||
    this->game_mode = prototype->duplicate(true);
 | 
			
		||||
    Node *player_node = this->game_mode->get_player_scene()->instantiate();
 | 
			
		||||
    IPlayer *player = dynamic_cast<IPlayer*>(player_node);
 | 
			
		||||
    if(player != nullptr) {
 | 
			
		||||
        this->initialize_player(player);
 | 
			
		||||
    } else {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,21 +26,21 @@ public:
 | 
			
		|||
    virtual void _enter_tree() override;
 | 
			
		||||
    virtual void _exit_tree() override;
 | 
			
		||||
 | 
			
		||||
    void set_game_mode(Ref<GameMode> mode);
 | 
			
		||||
    Ref<GameMode> get_game_mode() const;
 | 
			
		||||
 | 
			
		||||
    void player_connected();
 | 
			
		||||
    void player_disconnected();
 | 
			
		||||
    bool initialize_player(IPlayer *player);
 | 
			
		||||
    bool initialize_player(IPlayer *player, uint32_t id);
 | 
			
		||||
 | 
			
		||||
    void reset_game_mode();
 | 
			
		||||
protected:
 | 
			
		||||
    void grab_singleton();
 | 
			
		||||
    void release_singleton();
 | 
			
		||||
    uint32_t find_empty_player_slot() const;
 | 
			
		||||
    void set_game_mode(Ref<GameMode> prototype);
 | 
			
		||||
    virtual IPlayer *spawn_player(uint32_t id);
 | 
			
		||||
protected:
 | 
			
		||||
    static GameRoot *singleton_instance;
 | 
			
		||||
 | 
			
		||||
    uint32_t next_player_id{0};
 | 
			
		||||
    uint32_t next_player_id{1}; // 0 is the "invalid" player id
 | 
			
		||||
    HashMap<uint32_t, Pair<PlayerInput*, IPlayer*>> players{};
 | 
			
		||||
    Ref<GameMode> game_mode{};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -58,9 +58,10 @@ public:
 | 
			
		|||
 | 
			
		||||
    void set_first_boot_level(Ref<PackedScene> level);
 | 
			
		||||
    Ref<PackedScene> get_first_boot_level() const;
 | 
			
		||||
protected:
 | 
			
		||||
    virtual IPlayer *spawn_player(uint32_t id) override;
 | 
			
		||||
private:
 | 
			
		||||
    static bool is_valid_level(Ref<PackedScene> &level);
 | 
			
		||||
    void change_game_mode(Ref<GameMode> prototype);
 | 
			
		||||
private:
 | 
			
		||||
    HashMap<StringName, Level3D*> levels{};
 | 
			
		||||
    HashSet<SpawnPoint3D*> spawn_points{};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue