support chat commands on proxy server

This commit is contained in:
Martin Michelsen
2022-06-25 12:17:43 -07:00
parent fc078a5d51
commit ba1a25036b
21 changed files with 1297 additions and 1059 deletions
+41 -119
View File
@@ -24,41 +24,25 @@
#include "ReceiveCommands.hh"
using namespace std;
using namespace std::placeholders;
void Server::disconnect_client(struct bufferevent* bev) {
this->disconnect_client(this->bev_to_client.at(bev));
}
void Server::disconnect_client(shared_ptr<Client> c) {
this->bev_to_client.erase(c->bev);
struct bufferevent* bev = c->bev;
c->bev = nullptr;
int fd = bufferevent_getfd(bev);
if (fd < 0) {
this->log(INFO, "Client on virtual connection %p disconnected", bev);
if (c->channel.is_virtual_connection) {
this->log(INFO, "Disconnecting client on virtual connection %p",
c->channel.bev.get());
} else {
this->log(INFO, "Client on fd %d disconnected", fd);
this->log(INFO, "Disconnecting client on fd %d",
bufferevent_getfd(c->channel.bev.get()));
}
// if the output buffer is not empty, move the client into the draining pool
// instead of disconnecting it, to make sure all the data gets sent
struct evbuffer* out_buffer = bufferevent_get_output(bev);
if (evbuffer_get_length(out_buffer) == 0) {
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
bufferevent_free(bev);
} else {
// the callbacks will free it when all the data is sent or the client
// disconnects
bufferevent_setcb(bev, nullptr,
Server::dispatch_on_disconnecting_client_output,
Server::dispatch_on_disconnecting_client_error, this);
bufferevent_disable(bev, EV_READ);
}
this->channel_to_client.erase(&c->channel);
c->channel.disconnect();
process_disconnect(this->state, c);
// c is destroyed here (process_disconnect should remove any other references
// to it, e.g. from Lobby objects)
}
void Server::dispatch_on_listen_accept(
@@ -73,25 +57,6 @@ void Server::dispatch_on_listen_error(struct evconnlistener* listener,
reinterpret_cast<Server*>(ctx)->on_listen_error(listener);
}
void Server::dispatch_on_client_input(struct bufferevent* bev, void* ctx) {
reinterpret_cast<Server*>(ctx)->on_client_input(bev);
}
void Server::dispatch_on_client_error(struct bufferevent* bev, short events,
void* ctx) {
reinterpret_cast<Server*>(ctx)->on_client_error(bev, events);
}
void Server::dispatch_on_disconnecting_client_output(struct bufferevent* bev,
void* ctx) {
reinterpret_cast<Server*>(ctx)->on_disconnecting_client_output(bev);
}
void Server::dispatch_on_disconnecting_client_error(struct bufferevent* bev,
short events, void* ctx) {
reinterpret_cast<Server*>(ctx)->on_disconnecting_client_error(bev, events);
}
void Server::on_listen_accept(struct evconnlistener* listener,
evutil_socket_t fd, struct sockaddr*, int) {
@@ -111,13 +76,12 @@ void Server::on_listen_accept(struct evconnlistener* listener,
struct bufferevent *bev = bufferevent_socket_new(this->base.get(), fd,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
shared_ptr<Client> c(new Client(bev, listening_socket->version,
listening_socket->behavior));
this->bev_to_client.emplace(make_pair(bev, c));
bufferevent_setcb(bev, &Server::dispatch_on_client_input, nullptr,
&Server::dispatch_on_client_error, this);
bufferevent_enable(bev, EV_READ | EV_WRITE);
shared_ptr<Client> c(new Client(
bev, listening_socket->version, listening_socket->behavior));
c->channel.on_command_received = Server::on_client_input;
c->channel.on_error = Server::on_client_error;
c->channel.context_obj = this;
this->channel_to_client.emplace(&c->channel, c);
process_connect(this->state, c);
}
@@ -128,19 +92,19 @@ void Server::connect_client(
this->log(INFO, "Client connected on virtual connection %p", bev);
shared_ptr<Client> c(new Client(bev, version, initial_state));
this->bev_to_client.emplace(make_pair(bev, c));
c->channel.on_command_received = Server::on_client_input;
c->channel.on_error = Server::on_client_error;
c->channel.context_obj = this;
this->channel_to_client.emplace(&c->channel, c);
// Manually set the remote address, since the bufferevent has no fd and the
// Client constructor can't figure out the virtual remote address
auto* sin = reinterpret_cast<sockaddr_in*>(&c->remote_addr);
// Channel constructor can't figure out the virtual remote address
auto* sin = reinterpret_cast<sockaddr_in*>(&c->channel.remote_addr);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(address);
sin->sin_port = htons(port);
bufferevent_setcb(bev, &Server::dispatch_on_client_input, nullptr,
&Server::dispatch_on_client_error, this);
bufferevent_enable(bev, EV_READ | EV_WRITE);
process_connect(this->state, c);
}
@@ -151,73 +115,31 @@ void Server::on_listen_error(struct evconnlistener* listener) {
event_base_loopexit(this->base.get(), nullptr);
}
void Server::on_client_input(struct bufferevent* bev) {
shared_ptr<Client> c;
try {
c = this->bev_to_client.at(bev);
} catch (const out_of_range& e) {
this->log(WARNING, "Received message from client with no configuration");
// ignore all the data
// TODO: we probably should disconnect them or something
struct evbuffer* in_buffer = bufferevent_get_input(bev);
evbuffer_drain(in_buffer, evbuffer_get_length(in_buffer));
return;
}
void Server::on_client_input(Channel& ch, uint16_t command, uint32_t flag, std::string& data) {
Server* server = reinterpret_cast<Server*>(ch.context_obj);
shared_ptr<Client> c = server->channel_to_client.at(&ch);
if (c->should_disconnect) {
this->disconnect_client(bev);
return;
}
c->last_recv_time = now();
this->receive_and_process_commands(c);
if (c->should_disconnect) {
this->disconnect_client(bev);
return;
server->disconnect_client(c);
} else {
process_command(server->state, c, command, flag, data);
if (c->should_disconnect) {
server->disconnect_client(c);
}
}
}
void Server::on_disconnecting_client_output(struct bufferevent* bev) {
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
bufferevent_free(bev);
}
void Server::on_client_error(Channel& ch, short events) {
Server* server = reinterpret_cast<Server*>(ch.context_obj);
shared_ptr<Client> c = server->channel_to_client.at(&ch);
void Server::on_client_error(struct bufferevent* bev, short events) {
if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
this->log(WARNING, "Client caused error %d (%s)", err,
server->log(WARNING, "Client caused error %d (%s)", err,
evutil_socket_error_to_string(err));
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
this->disconnect_client(bev);
}
}
void Server::on_disconnecting_client_error(struct bufferevent* bev,
short events) {
if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
this->log(WARNING, "Disconnecting client caused error %d (%s)", err,
evutil_socket_error_to_string(err));
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
bufferevent_flush(bev, EV_WRITE, BEV_FINISHED);
bufferevent_free(bev);
}
}
void Server::receive_and_process_commands(shared_ptr<Client> c) {
try {
for_each_received_command(c->bev, c->version, c->crypt_in.get(),
[this, c](uint16_t command, uint16_t flag, const std::string& data) {
process_command(this->state, c, command, flag, data);
});
} catch (const exception& e) {
this->log(INFO, "Error in client stream: %s", e.what());
c->should_disconnect = true;
return;
server->disconnect_client(c);
}
}
@@ -275,11 +197,11 @@ void Server::add_socket(
}
shared_ptr<Client> Server::get_client() const {
if (this->bev_to_client.empty()) {
if (this->channel_to_client.empty()) {
throw runtime_error("no clients on game server");
}
if (this->bev_to_client.size() > 1) {
if (this->channel_to_client.size() > 1) {
throw runtime_error("multiple clients on game server");
}
return this->bev_to_client.begin()->second;
}
return this->channel_to_client.begin()->second;
}