Skip to content

Commit

Permalink
Apply PIMPL to Mumble::Client
Browse files Browse the repository at this point in the history
  • Loading branch information
promi committed Mar 4, 2017
1 parent ed77164 commit 3d52ab8
Show file tree
Hide file tree
Showing 5 changed files with 520 additions and 423 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ libmumble_pluginbot_plusplus_mumble_la_SOURCES = \
src/mumble/cert-manager.cc \
src/mumble/channel.cc \
src/mumble/client.cc \
src/mumble/client-impl.cc \
src/mumble/connection.cc \
src/mumble/img-reader.cc \
src/mumble/messages.cc \
Expand Down
267 changes: 267 additions & 0 deletions src/mumble/client-impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
/*
mumble-pluginbot-plusplus - An extensible Mumble bot
Copyright (c) 2012 Matthew Perry (mattvperry)
Copyright (c) 2013 Jeromy Maligie (Kingles)
Copyright (c) 2013 Matthew Perry (mattvperry)
Copyright (c) 2014 Aaron Herting (qwertos)
Copyright (c) 2014 Benjamin Neff (SuperTux88)
Copyright (c) 2014 Jack Chen (chendo)
Copyright (c) 2014 Matthew Perry (mattvperry)
Copyright (c) 2014 Meister der Bots
Copyright (c) 2014 dafoxia
Copyright (c) 2015 dafoxia
Copyright (c) 2015 loscoala
Copyright (c) 2016 Phobos (promi) <[email protected]>
Copyright (c) 2017 Phobos (promi) <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mumble/client-impl.hh"

namespace Mumble
{
Client::Impl::Impl (const Aither::Log &log, const Configuration &config,
const Certificate &cert, const std::string &client_identification)
: m_log (log), m_config (config), m_cert (cert),
m_client_identification (client_identification)
{
}

Client::Impl::~Impl ()
{
m_read_thread_running = false;
m_ping_thread_running = false;
m_read_thread.join ();
m_ping_thread.join ();
}

bool Client::Impl::connect ()
{
m_connection = std::make_unique<Connection> (m_log, m_config.host,
m_config.port,
m_cert);
m_connection->connect ();
if (!m_connection->connected ())
{
return false;
}
init_callbacks ();
version_exchange ();
authenticate ();
m_connected = true;
m_read_thread_running = true;
m_read_thread = std::thread ([this] { read (); });
m_ping_thread_running = true;
m_ping_thread = std::thread ([this] { ping (); });
return m_connected;
}

void Client::Impl::disconnect ()
{
m_connection->disconnect ();
m_synced = false;
m_ready = false;
m_connected = false;
m_read_thread_running = false;
m_read_thread.join ();
m_ping_thread_running = false;
m_ping_thread.join ();
}

void Client::Impl::on (int type,
std::function<void(const ::google::protobuf::Message&)> f)
{
// TODO: Use libsigc++ in the future, also enables unregistering!
m_callbacks[type].push_back (f);
}

void Client::Impl::send (int type, const ::google::protobuf::Message& msg)
{
m_connection->send_message (type, msg);
}

void Client::Impl::read ()
{
while (m_read_thread_running)
{
try
{
auto pair = m_connection->read_message ();
run_callbacks (pair.first, *pair.second);
}
catch (const std::string &s)
{
AITHER_ERROR("Could not read message from server: " << s << "\n");
throw;
}
}
}

void Client::Impl::ping ()
{
using namespace std::chrono_literals;
using namespace std::chrono;

while (m_ping_thread_running)
{
MumbleProto::Ping msg;
msg.set_timestamp (duration_cast<milliseconds>
(system_clock::now ().time_since_epoch ()).count ());
send (msg);
std::this_thread::sleep_for (15s);
}
}

void Client::Impl::run_callbacks (int type,
const ::google::protobuf::Message& msg)
{
auto cbs = m_callbacks.find (type);
if (cbs != std::end (m_callbacks))
{
for (auto f : cbs->second)
{
f (msg);
}
}
}

void Client::Impl::init_callbacks ()
{
on<MumbleProto::ServerSync> ([this] (auto msg)
{
if (msg.has_max_bandwidth ())
{
m_max_bandwidth = msg.max_bandwidth ();
}
m_session = msg.session ();
m_synced = true;
for (auto f : m_connected_callbacks)
{
f ();
}
});
on<MumbleProto::ChannelState> ([this] (auto msg)
{
auto channel_id = msg.channel_id ();
auto channel = m_channels.find (channel_id);
if (channel == std::end (m_channels))
{
m_channels.emplace (channel_id, msg);
}
else
{
channel->second.update (msg);
}
});
on<MumbleProto::ChannelRemove> ([this] (auto msg)
{
m_channels.erase (msg.channel_id ());
});
on<MumbleProto::UserState> ([this] (auto msg)
{
auto session = msg.session ();
auto user = m_users.find (session);
if (user == std::end (m_users))
{
m_users.emplace (session, msg);
}
else
{
user->second.update (msg);
}
});
on<MumbleProto::UserRemove> ([this] (auto msg)
{
auto session = msg.session ();
if (session == m_session)
{
this->disconnect ();
}
m_users.erase (session);
});
on<MumbleProto::CodecVersion> ([this] (auto msg)
{
this->codec_version (msg);
});
on<MumbleProto::Ping> ([this] (auto msg)
{
using namespace std::chrono;
using namespace std::chrono_literals;
m_pingtime = (duration_cast<milliseconds>
(system_clock::now ().time_since_epoch ()).count ()) - msg.timestamp ();
m_ready = true;
});
on<MumbleProto::CryptSetup> ([this] (auto msg)
{
// For later implementation of UDP communication
(void) msg;
});
on<MumbleProto::Reject> ([this] (auto msg)
{
m_rejectmessage = std::make_unique<MumbleProto::Reject> (msg);
this->disconnect ();
});
on<MumbleProto::ServerConfig> ([this] (auto msg)
{
(void) msg;
});
}

void Client::Impl::version_exchange ()
{
MumbleProto::Version msg;
msg.set_version (encode_version (1, 3, 0));
msg.set_release (m_client_identification);
msg.set_os ("Unknown");
msg.set_os_version ("Unknown");
// os: %x{uname -s -m}.strip,
// os_version: %x{uname -v}.strip
send (msg);
}

void Client::Impl::authenticate ()
{
// encoder = Celt::Encoder.new 32000, 180, 1, 1
MumbleProto::Authenticate msg;
msg.set_username (m_config.username);
msg.set_password (m_config.password);
// celt_versions: [encoder.bitstream_version],
msg.set_opus (true);
send (msg);
}

void Client::Impl::codec_version (const MumbleProto::CodecVersion &message)
{
if (message.has_opus ())
{
m_codec = Codec::opus;
m_codec_usable = true;
}
else
{
m_codec_usable = false;
/*
encoder = Celt::Encoder.new 32000, 180, 1, 1
@codec = [Codec::alpha, Codec::beta][[message.alpha, message.beta].index(encoder.bitstream_version)]
encoder.destroy
*/
}
}

uint32_t Client::Impl::encode_version(int major, int minor, int patch)
{
return (major << 16) | (minor << 8) | (patch & 0xff);
}
}
99 changes: 99 additions & 0 deletions src/mumble/client-impl.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
/*
mumble-pluginbot-plusplus - An extensible Mumble bot
Copyright (c) 2012 Matthew Perry (mattvperry)
Copyright (c) 2013 Jeromy Maligie (Kingles)
Copyright (c) 2013 Matthew Perry (mattvperry)
Copyright (c) 2014 Aaron Herting (qwertos)
Copyright (c) 2014 Benjamin Neff (SuperTux88)
Copyright (c) 2014 Jack Chen (chendo)
Copyright (c) 2014 Matthew Perry (mattvperry)
Copyright (c) 2014 Meister der Bots
Copyright (c) 2014 dafoxia
Copyright (c) 2015 dafoxia
Copyright (c) 2015 loscoala
Copyright (c) 2016 Phobos (promi) <[email protected]>
Copyright (c) 2017 Phobos (promi) <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once

#include "mumble/client.hh"

namespace Mumble
{
struct Client::Impl
{
const Aither::Log &m_log;
const Configuration &m_config;
const Certificate &m_cert;
const std::string m_client_identification;
std::map<uint32_t, User> m_users;
std::map<uint32_t, Channel> m_channels;
bool m_ready = false;
Codec m_codec = Codec::opus;
bool m_codec_usable = false;
int m_max_bandwidth = 0;
std::unique_ptr<MumbleProto::Reject> m_rejectmessage = nullptr;
int m_pingtime = 0;
bool m_connected = false;
bool m_synced = false;
std::unique_ptr<Connection> m_connection;
std::map<int,
std::list<
std::function<void(const ::google::protobuf::Message&)>>> m_callbacks;
std::list<std::function<void()>> m_connected_callbacks;
std::thread m_read_thread;
std::thread m_ping_thread;
bool m_read_thread_running;
bool m_ping_thread_running;
uint32_t m_session;

Impl (const Aither::Log &log, const Configuration &config,
const Certificate &cert, const std::string &client_identification);
~Impl ();
bool connect ();
void disconnect ();
User& text_user_internal (uint32_t session_id, const std::string &message_text);
User& text_user_img (uint32_t session_id, const FileSystem::path &file);
Channel& text_channel_internal (uint32_t channel_id,
const std::string &message_text);
Channel& text_channel_img (uint32_t channel_id, const FileSystem::path &file);
void on (int type, std::function<void(const ::google::protobuf::Message&)> f);
template <class T>
inline void on (std::function<void(const T&)> f)
{
on (Messages::sym_to_type.at (std::type_index (typeid (T))), [f] (
const auto &message)
{
f (dynamic_cast<const T&>(message));
});
}
void send (int type, const ::google::protobuf::Message& msg);
template <class T>
inline void send (const T &msg)
{
send (Messages::sym_to_type.at (std::type_index (typeid (T))), msg);
}
void read ();
void ping ();
void run_callbacks (int type, const ::google::protobuf::Message& msg);
void init_callbacks ();
void version_exchange ();
void authenticate ();
void codec_version (const MumbleProto::CodecVersion &message);
static uint32_t encode_version(int major, int minor, int patch);
};
}
Loading

0 comments on commit 3d52ab8

Please sign in to comment.