enable most shell commands to affect a specific session

This commit is contained in:
Martin Michelsen
2022-12-26 21:33:38 -08:00
parent de7239e3fb
commit dfad80eb9a
6 changed files with 129 additions and 23 deletions
+12
View File
@@ -795,6 +795,18 @@ shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session() {
return this->id_to_session.begin()->second;
}
shared_ptr<ProxyServer::LinkedSession> ProxyServer::get_session_by_name(
const std::string& name) {
try {
uint64_t session_id = stoull(name, nullptr, 16);
return this->id_to_session.at(session_id);
} catch (const invalid_argument&) {
throw runtime_error("invalid session name");
} catch (const out_of_range&) {
throw runtime_error("no such session");
}
}
shared_ptr<ProxyServer::LinkedSession> ProxyServer::create_licensed_session(
shared_ptr<const License> l, uint16_t local_port, GameVersion version,
const ClientConfigBB& newserv_client_config) {
+1
View File
@@ -152,6 +152,7 @@ public:
};
std::shared_ptr<LinkedSession> get_session();
std::shared_ptr<LinkedSession> get_session_by_name(const std::string& name);
std::shared_ptr<LinkedSession> create_licensed_session(
std::shared_ptr<const License> l,
uint16_t local_port,
+48
View File
@@ -270,6 +270,54 @@ shared_ptr<Client> Server::get_client() const {
return this->channel_to_client.begin()->second;
}
vector<shared_ptr<Client>> Server::get_clients_by_identifier(const string& ident) const {
int64_t serial_number_hex = -1;
int64_t serial_number_dec = -1;
try {
serial_number_dec = stoul(ident, nullptr, 10);
} catch (const invalid_argument&) { }
try {
serial_number_hex = stoul(ident, nullptr, 16);
} catch (const invalid_argument&) { }
u16string u16name = decode_sjis(ident);
// TODO: It's kind of not great that we do a linear search here, but this is
// only used in the shell, so it should be pretty rare.
vector<shared_ptr<Client>> results;
for (const auto& it : this->channel_to_client) {
auto c = it.second;
if (c->license && c->license->serial_number == serial_number_dec) {
results.emplace_back(move(c));
continue;
}
if (c->license && c->license->serial_number == serial_number_hex) {
results.emplace_back(move(c));
continue;
}
if (c->license && c->license->username == ident) {
results.emplace_back(move(c));
continue;
}
auto p = c->game_data.player(false);
if (p && p->disp.name == u16name) {
results.emplace_back(move(c));
continue;
}
if (c->channel.name == ident) {
results.emplace_back(move(c));
continue;
}
if (starts_with(c->channel.name, ident + " ")) {
results.emplace_back(move(c));
continue;
}
}
return results;
}
shared_ptr<struct event_base> Server::get_base() const {
return this->base;
}
+2
View File
@@ -31,6 +31,8 @@ public:
GameVersion version, ServerBehavior initial_state);
std::shared_ptr<Client> get_client() const;
std::vector<std::shared_ptr<Client>> get_clients_by_identifier(
const std::string& ident) const;
std::shared_ptr<struct event_base> get_base() const;
private:
+64 -22
View File
@@ -24,11 +24,14 @@ void ServerShell::print_prompt() {
fflush(stdout);
}
shared_ptr<ProxyServer::LinkedSession> ServerShell::get_proxy_session() {
shared_ptr<ProxyServer::LinkedSession> ServerShell::get_proxy_session(
const string& name) {
if (!this->state->proxy_server.get()) {
throw runtime_error("the proxy server is disabled");
}
return this->state->proxy_server->get_session();
return name.empty()
? this->state->proxy_server->get_session()
: this->state->proxy_server->get_session_by_name(name);
}
static void set_boolean(bool* target, const string& args) {
@@ -68,12 +71,23 @@ static string get_quoted_string(string& s) {
}
void ServerShell::execute_command(const string& command) {
// find the entry in the command table and run the command
// Find the entry in the command table and run the command
size_t command_end = skip_non_whitespace(command, 0);
size_t args_begin = skip_whitespace(command, command_end);
string command_name = command.substr(0, command_end);
string command_args = command.substr(args_begin);
string session_name;
if (command_name == "on") {
size_t session_name_end = skip_non_whitespace(command_args, 0);
size_t command_begin = skip_whitespace(command_args, session_name_end);
command_end = skip_non_whitespace(command_args, command_begin);
args_begin = skip_whitespace(command_args, command_end);
session_name = command_args.substr(0, session_name_end);
command_name = command_args.substr(command_begin, command_end - command_begin);
command_args = command_args.substr(args_begin);
}
if (command_name == "exit") {
throw exit_shell();
@@ -144,9 +158,10 @@ Server commands:\n\
Show the current state of a tournament. The quotes are required unless the\n\
tournament name contains no spaces.\n\
\n\
Proxy commands (these will only work when exactly one client is connected):\n\
Proxy commands:\n\
sc <data>\n\
Send a command to the client.\n\
Send a command to the client. This command also can be used to send data to\n\
a client on the game server.\n\
ss <data>\n\
Send a command to the server.\n\
chat <text>\n\
@@ -203,6 +218,15 @@ Proxy commands (these will only work when exactly one client is connected):\n\
Set the next item to be dropped as if the client had run the $item command.\n\
close-idle-sessions\n\
Closes all sessions that don\'t have a client and server connected.\n\
\n\
If there are multiple clients connected, or multiple proxy sessions open, many\n\
of the above commands will fail since they can\'t determine which session should\n\
be affected. To specify a session, prefix the command with `on <ident>`. For\n\
game server sessions, <ident> may be the client\'s ID (e.g. C-3), player name,\n\
license serial number (specified in hex or in decimal), or BB account username.\n\
For proxy sessions, <ident> may only be the session ID (which is generally the\n\
same as the client\'s serial number). For example, to send a ping to the proxy\n\
session with ID 17205AE4, run the command `on 17205AE4 sc 1D 00 04 00`.\n\
");
@@ -473,7 +497,7 @@ Proxy commands (these will only work when exactly one client is connected):\n\
shared_ptr<ProxyServer::LinkedSession> proxy_session;
try {
proxy_session = this->get_proxy_session();
proxy_session = this->get_proxy_session(session_name);
} catch (const exception&) { }
if (proxy_session.get()) {
@@ -488,12 +512,30 @@ Proxy commands (these will only work when exactly one client is connected):\n\
throw runtime_error("cannot send to server in non-proxy session");
}
auto c = this->state->game_server->get_client();
send_command_with_header(c->channel, data.data(), data.size());
shared_ptr<Client> c;
if (session_name.empty()) {
c = this->state->game_server->get_client();
} else {
auto clients = this->state->game_server->get_clients_by_identifier(
session_name);
if (clients.empty()) {
throw runtime_error("no such client");
}
if (clients.size() > 1) {
throw runtime_error("multiple clients found");
}
c = move(clients[0]);
}
if (c) {
send_command_with_header(c->channel, data.data(), data.size());
} else {
throw runtime_error("no client available");
}
}
} else if ((command_name == "chat") || (command_name == "dchat")) {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
string data(8, '\0');
data.push_back('\x09');
@@ -509,18 +551,18 @@ Proxy commands (these will only work when exactly one client is connected):\n\
session->server_channel.send(0x06, 0x00, data);
} else if (command_name == "marker") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
session->server_channel.send(0x89, stoul(command_args));
} else if (command_name == "warp") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
uint8_t area = stoul(command_args);
send_warp(session->client_channel, session->lobby_client_id, area);
send_warp(session->server_channel, session->lobby_client_id, area);
} else if ((command_name == "info-board") || (command_name == "info-board-data")) {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
string data;
if (command_name == "info-board-data") {
@@ -534,7 +576,7 @@ Proxy commands (these will only work when exactly one client is connected):\n\
session->server_channel.send(0xD9, 0x00, data);
} else if (command_name == "set-override-section-id") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
if (command_args.empty()) {
session->options.override_section_id = -1;
} else {
@@ -542,7 +584,7 @@ Proxy commands (these will only work when exactly one client is connected):\n\
}
} else if (command_name == "set-override-event") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
if (command_args.empty()) {
session->options.override_lobby_event = -1;
} else {
@@ -556,7 +598,7 @@ Proxy commands (these will only work when exactly one client is connected):\n\
}
} else if (command_name == "set-override-lobby-number") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
if (command_args.empty()) {
session->options.override_lobby_number = -1;
} else {
@@ -564,27 +606,27 @@ Proxy commands (these will only work when exactly one client is connected):\n\
}
} else if (command_name == "set-chat-filter") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
set_boolean(&session->options.enable_chat_filter, command_args);
} else if (command_name == "set-infinite-hp") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
set_boolean(&session->options.infinite_hp, command_args);
} else if (command_name == "set-infinite-tp") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
set_boolean(&session->options.infinite_tp, command_args);
} else if (command_name == "set-switch-assist") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
set_boolean(&session->options.switch_assist, command_args);
} else if (command_name == "set-save-files" && this->state->proxy_allow_save_files) {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
set_boolean(&session->options.save_files, command_args);
} else if (command_name == "set-block-function-calls") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
if (command_args.empty()) {
session->options.function_call_return_value = -1;
} else {
@@ -592,7 +634,7 @@ Proxy commands (these will only work when exactly one client is connected):\n\
}
} else if (command_name == "set-next-item") {
auto session = this->get_proxy_session();
auto session = this->get_proxy_session(session_name);
if (session->version == GameVersion::BB) {
throw runtime_error("proxy session is BB");
+2 -1
View File
@@ -26,7 +26,8 @@ public:
protected:
std::shared_ptr<ServerState> state;
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session();
std::shared_ptr<ProxyServer::LinkedSession> get_proxy_session(
const std::string& name);
virtual void print_prompt();
virtual void execute_command(const std::string& command);