Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ MultiConsoleSystemOption::MultiConsoleSystemOption(
size_t min_consoles,
size_t max_consoles,
size_t consoles,
std::function<std::unique_ptr<ConsoleSystemOption>()> console_factory
std::function<std::unique_ptr<ConsoleSystemOption>()> factory
)
: m_min_consoles(std::max(min_consoles, (size_t)1))
, m_max_consoles(std::min(max_consoles, (size_t)MAX_CONSOLES))
, m_active_consoles(0)
, m_consoles(m_max_consoles)
{
for (size_t c = 0; c < m_max_consoles; c++){
m_consoles.emplace_back(console_factory());
m_consoles.emplace_back(factory());
}
consoles = std::max(consoles, m_min_consoles);
consoles = std::min(consoles, m_max_consoles);
Expand All @@ -40,9 +40,9 @@ MultiConsoleSystemOption::MultiConsoleSystemOption(
size_t min_consoles,
size_t max_consoles,
const JsonValue& json,
std::function<std::unique_ptr<ConsoleSystemOption>()> console_factory
std::function<std::unique_ptr<ConsoleSystemOption>()> factory
)
: MultiConsoleSystemOption(min_consoles, max_consoles, 0, std::move(console_factory))
: MultiConsoleSystemOption(min_consoles, max_consoles, 0, std::move(factory))
{
MultiConsoleSystemOption::load_json(json);
}
Expand Down
12 changes: 8 additions & 4 deletions SerialPrograms/Source/GameConsole/MultiConsoleSystemOption.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
*
*/

#ifndef PokemonAutomation_ConsoleInfra_MultiConsoleSystemOption_H
#define PokemonAutomation_ConsoleInfra_MultiConsoleSystemOption_H
#ifndef PokemonAutomation_GameConsole_MultiConsoleSystemOption_H
#define PokemonAutomation_GameConsole_MultiConsoleSystemOption_H

#include <functional>
#include "Common/Cpp/Containers/FixedLimitVector.h"
Expand All @@ -25,13 +25,17 @@ class MultiConsoleSystemOption{
size_t min_consoles,
size_t max_consoles,
size_t consoles,
std::function<std::unique_ptr<ConsoleSystemOption>()> console_factory
std::function<std::unique_ptr<ConsoleSystemOption>()> factory = []{
return std::make_unique<ConsoleSystemOption>(1);
}
);
MultiConsoleSystemOption(
size_t min_consoles,
size_t max_consoles,
const JsonValue& json,
std::function<std::unique_ptr<ConsoleSystemOption>()> console_factory
std::function<std::unique_ptr<ConsoleSystemOption>()> factory = []{
return std::make_unique<ConsoleSystemOption>(1);
}
);

void resize(size_t count){
Expand Down
159 changes: 159 additions & 0 deletions SerialPrograms/Source/GameConsole/MultiConsoleSystemSession.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* Multi-Console System Session
*
* From: https://github.com/PokemonAutomation/
*
*/

#include "Common/Cpp/Logging/GlobalLogger.h"
#include "Common/Cpp/EarlyShutdown.h"
#include "Common/Cpp/Containers/FixedLimitVector.tpp"
#include "CommonFramework/Logging/Logger.h"
#include "MultiConsoleSystemSession.h"

//#include <iostream>
//using std::cout;
//using std::endl;

namespace PokemonAutomation{
namespace GameConsole{


void MultiConsoleSystemSession::add_listener(Listener& listener){
m_listeners.add(listener);
}
void MultiConsoleSystemSession::remove_listener(Listener& listener){
m_listeners.remove(listener);
}

bool MultiConsoleSystemSession::try_shutdown(){
bool success = true;
for (std::unique_ptr<ConsoleSystemSession>& console : m_consoles){
success &= console->try_shutdown();
}
return success;
}
MultiConsoleSystemSession::~MultiConsoleSystemSession(){
blocking_shutdown(
global_logger_tagged(),
"MultiConsoleSystemSession",
[this]{ return try_shutdown(); }
);
}

MultiConsoleSystemSession::MultiConsoleSystemSession(
MultiConsoleSystemOption& option,
bool allow_commands_while_locked,
std::optional<uint64_t> program_tracking_id
)
: MultiConsoleSystemSession(
option,
[allow_commands_while_locked, program_tracking_id](ConsoleSystemOption& option, size_t console_index){
return std::make_unique<ConsoleSystemSession>(
global_logger_raw(),
option,
allow_commands_while_locked,
console_index,
program_tracking_id
);
}
)
{}
MultiConsoleSystemSession::MultiConsoleSystemSession(
MultiConsoleSystemOption& option,
std::function<
std::unique_ptr<ConsoleSystemSession>(
ConsoleSystemOption& option,
size_t console_index
)
> factory
)
: m_option(option)
, m_consoles(option.active_consoles())
, m_factory(std::move(factory))
{
size_t count = option.active_consoles();
for (size_t c = 0; c < count; c++){
m_consoles.emplace_back(m_factory(option[c], c));
}
}

void MultiConsoleSystemSession::lock_controllers(const std::string& reason){
std::unique_lock<Mutex> lg1(m_state_lock);
m_listeners.run_method(&Listener::on_console_count_lock, true);
for (std::unique_ptr<ConsoleSystemSession>& console : m_consoles){
console->lock_controllers(reason);
}
}
void MultiConsoleSystemSession::unlock_controllers(){
std::unique_lock<Mutex> lg1(m_state_lock);
for (std::unique_ptr<ConsoleSystemSession>& console : m_consoles){
console->unlock_controllers();
}
m_listeners.run_method(&Listener::on_console_count_lock, false);
}


void MultiConsoleSystemSession::add_console_count_lock(Listener& listener){
// cout << "add_console_count_lock()" << endl;
std::lock_guard<Mutex> lg(m_state_lock);
m_count_locks.insert(&listener);
}
void MultiConsoleSystemSession::remove_console_count_lock(Listener& listener){
// cout << "remove_console_count_lock()" << endl;
std::lock_guard<Mutex> lg(m_state_lock);
m_count_locks.erase(&listener);
}

void MultiConsoleSystemSession::set_active_consoles(size_t count){
{
std::lock_guard<Mutex> lg(m_state_lock);
if (count == m_option.active_consoles() &&
count == m_consoles.size()
){
return;
}
}

std::lock_guard<Mutex> lg0(m_resize_lock);

// Signal all listeners to drop locks.
m_listeners.run_method(&Listener::shutdown);

{
std::unique_lock<Mutex> lg1(m_state_lock);

// Wait for all locks to be dropped.
m_cv.wait(lg1, [this]{
return m_count_locks.empty();
});

m_consoles.reset(count);
m_option.resize(count);
for (size_t c = 0; c < count; c++){
m_consoles.emplace_back(m_factory(m_option[c], c));
}
}

// Signal all listeners it's ok to re-lock.
m_listeners.run_method(&Listener::startup, count);
}


JsonValue MultiConsoleSystemSession::to_json() const{
std::lock_guard<Mutex> lg(m_state_lock);
return m_option.to_json();
}
void MultiConsoleSystemSession::load_json(const JsonValue& json){
// cout << "MultiConsoleSystemSession::load_json()" << endl;
{
std::lock_guard<Mutex> lg(m_state_lock);
m_option.load_json(json);
}
set_active_consoles(m_option.active_consoles());
}




}
}
113 changes: 113 additions & 0 deletions SerialPrograms/Source/GameConsole/MultiConsoleSystemSession.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* Multi-Console System Session
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_GameConsole_MultiConsoleSystemSession_H
#define PokemonAutomation_GameConsole_MultiConsoleSystemSession_H

#include "Common/Cpp/ListenerSet.h"
#include "Common/Cpp/UiWrapper.h"
#include "Common/Cpp/Containers/FixedLimitVector.h"
#include "GameConsole/ConsoleSystemSession.h"
#include "MultiConsoleSystemOption.h"

namespace PokemonAutomation{
namespace GameConsole{


class MultiConsoleSystemSession : public UiState<MultiConsoleSystemSession>{
public:
struct Listener{
// Called when the console count is locked or unlocked.
virtual void on_console_count_lock(bool locked) = 0;

// Sent before the console sessions are destroyed. Listeners should
// drop their references to them before returning.
virtual void shutdown() = 0;

// Sent after new consoles are started up.
// This is called immediately when attaching a listener to give the
// current console count. The listener must drop all references to the
// console sessions before detaching.
virtual void startup(size_t console_count) = 0;
};
void add_listener(Listener& listener);
void remove_listener(Listener& listener);


public:
bool try_shutdown();
virtual ~MultiConsoleSystemSession();

MultiConsoleSystemSession(
MultiConsoleSystemOption& option,
bool allow_commands_while_locked,
std::optional<uint64_t> program_tracking_id = {}
);
MultiConsoleSystemSession(
MultiConsoleSystemOption& option,
std::function<
std::unique_ptr<ConsoleSystemSession>(
ConsoleSystemOption& option,
size_t console_index
)
> factory
);


public:
// These are not thread-safe with "resize()".
size_t min_consoles() const{ return m_option.min_consoles(); }
size_t max_consoles() const{ return m_option.max_consoles(); }

size_t active_consoles() const{ return m_consoles.size(); }
ConsoleSystemSession& operator[](size_t index){ return *m_consoles[index]; }


public:
void lock_controllers(const std::string& reason);
void unlock_controllers();


public:
// Lock/unlock the console count.
void add_console_count_lock(Listener& listener);
void remove_console_count_lock(Listener& listener);

void set_active_consoles(size_t count);


public:
JsonValue to_json() const;
void load_json(const JsonValue& json);


private:
MultiConsoleSystemOption& m_option;

Mutex m_resize_lock;
FixedLimitVector<std::unique_ptr<ConsoleSystemSession>> m_consoles;

std::function<
std::unique_ptr<ConsoleSystemSession>(
ConsoleSystemOption& option,
size_t console_index
)
> m_factory;

// Listeners who are currently locking the console count.
// It is not safe to change the console count if this is not zero.
mutable Mutex m_state_lock;
ConditionVariable m_cv;
std::set<Listener*> m_count_locks;

ListenerSet<Listener> m_listeners;
};



}
}
#endif
Loading