maybe this would share the intricateness of the initial library: (not finished, specifically the server and client delegates webrtc message posting to webrtc data channel is not any complete)
(I think writing socketio code in c++ is not nice :D )
surely i written this code not neat/tidy.
but would fix that after i would finish it.
// planet3954-webrtc-cplusplus.cpp : Defines the exported functions for the DLL.
//
#include "pch.h"
#include <windows.h>
#include "framework.h"
#include "planet3954-webrtc-cplusplus.h"
//#include <api/peer_connection_interface.h>
//#include <api/create_peerconnection_factory.h>
#include "sio/sio_client.h"
#include "sio/sio_message.h"
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <thread>
//# pragma comment(lib, "secur32.lib")
//# pragma comment(lib, "winmm.lib")
//# pragma comment(lib, "dmoguids.lib")
//# pragma comment(lib, "wmcodecdspuuid.lib")
//# pragma comment(lib, "msdmo.lib")
//# pragma comment(lib, "Strmiids.lib")
//# pragma comment(lib, "webrtc.lib")
// This is an example of an exported variable
PLANET3954WEBRTCCPLUSPLUS_API int nplanet3954webrtccplusplus=0;
// This is an example of an exported function.
PLANET3954WEBRTCCPLUSPLUS_API int fnplanet3954webrtccplusplus(void)
{
//std::cout << "test" << std::endl;
return 0;
}
Cplanet3954webrtccplusplus::Cplanet3954webrtccplusplus(connection_listener_event_delegate& on_connect,
connection_listener_event_delegate& on_fail,
connection_listener_event_delegate& on_close,
sio::socket::event_listener_aux& on_message) : connection_listener_for_connect(on_connect),
connection_listener_for_fail(on_fail),
connection_listener_for_close(on_close),
message_listener(on_message),
internal_created_game(nullptr),
internal_retrieved_games(nullptr) {
init_webrtc();
}
bool Cplanet3954webrtccplusplus::init_webrtc() {
//_webrtc =WebRTCMain::getInstance(current_user_id);
//std::string dllpath("webrtc.dll");
//const wchar_t* pwstr = dllpath.c_str();
//HINSTANCE hGetProcIDDLL = LoadLibrary((LPCWSTR)pwstr);
HINSTANCE hGetProcIDDLL = LoadLibrary("webrtc.dll");
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
return false;
}
createWebRTC = (getinstance) GetProcAddress(hGetProcIDDLL, "getInstance");
if (!createWebRTC) {
std::cout << "could not locate the function" << std::endl;
return false;
}
on_wrtc__ice_candidate = [&](const std::string& origin, WebRTCIF::Ice& ice) {
/* std::string candidate;
std::string sdp_mid;
int sdp_mline_index;*/
sio::message::ptr p = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (p.get());
o->insert("candidate", ice.candidate);
o->insert("sdp_mid", ice.sdp_mid);
o->insert("sdp_mline_index", sio::int_message::create(ice.sdp_mline_index));
o->insert("user_id", origin);
if (is_creator_currently) {
WebRTCIF* tmp = nullptr;
sio::socket::ptr tmps = nullptr;
_lock_for_join_game.lock();
if (auto search = _webrtc.find(origin); search != _webrtc.end())
tmp = search->second;
if (tmp != nullptr) {
if (auto search = gc_joined_users.find(origin); search != gc_joined_users.end())
tmps = search->second;
}
if (tmps != nullptr) {
tmps->emit("ice", p);
}
//tbd
//fix add additional mutexes for game creator
_lock_for_join_game.unlock();
}
else {
_lock_for_join_game.lock();
ngc_joined_user_socket->emit("ice", p);
_lock_for_join_game.unlock();
}
};
on_wrtc__conn_state_changed = [&](const std::string&, WebRTCIF::PeerConnState s) {
switch (s) {
case WebRTCIF::PeerConnState::kFailed:
//tbd
//ask client to reinit
break;
case WebRTCIF::PeerConnState::kClosed:
//tbd
//check if were expected to be closed
default:
break;
}
};
on_wrtc__sdp_created = [&](const std::string& origin, std::string typ, std::string sdp) {
sio::message::ptr p = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (p.get());
o->insert("type", typ);
o->insert("sdp", sdp);
o->insert("user_id", origin);
if (is_creator_currently) {
WebRTCIF* tmp = nullptr;
sio::socket::ptr tmps = nullptr;
_lock_for_join_game.lock();
if (auto search = _webrtc.find(origin); search != _webrtc.end())
tmp = search->second;
if (tmp != nullptr) {
if (auto search = gc_joined_users.find(origin); search != gc_joined_users.end())
tmps = search->second;
}
if (tmps != nullptr) {
tmps->emit("sdp", p);
}
//tbd
//fix add additional mutexes for game creator
_lock_for_join_game.unlock();
}
else {
_lock_for_join_game.lock();
ngc_joined_user_socket->emit("sdp", p);
_lock_for_join_game.unlock();
}
};
on_wrtc__error = [&](const std::string&, WebRTCIF::Error, std::string) {
//tbd error handler
};
on_wrtc__message = [&](const std::string&, std::string&& ) {
//tbd
//message receival
};
d_server_func = [&]() {
while (server_to_func) {
_d_server->command();
Sleep(1);
}
};
d_client_func = [&]() {
while (client_to_func) {
for (auto c = _d_clients.begin(); c != _d_clients.end(); c++) {
c->second->command();
}
Sleep(1);
}
};
Cplanet3954webrtccplusplus* tmp = this;
on_wrtc__data_channel_opent = [&](const std::string&) {
//tbd
//implement notification to gc for that data channel has succeeded
//server_to_func = true; //tbd -> set somewhere and also add false setting somewhere
//tbd where to reset or reinit in case data connection occurs twice? implement that
if (is_creator_currently == false) {
_d_server = new server_delegate();
_d_server->register_post_data_func(std::bind(&Cplanet3954webrtccplusplus::on_post_data, this, std::placeholders::_1, std::placeholders::_2));
server_thd = new std::thread(d_server_func);
sio::message::ptr p = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (p.get());
o->insert("user_id", current_user_id);
ngc_joined_user_socket->emit("client_join_setup_completed", p);
}
else {
//tbd
}
};
return true;
}
bool Cplanet3954webrtccplusplus::on_post_data(unsigned int i, std::string&& data) {
package p(i, std::move(data));
std::string && m = p.serialize();
return false;
}
bool Cplanet3954webrtccplusplus::init_gc_joined_game_socket(sio::socket::ptr p) {
//game creator side's socket for receiving duplex messages from joined user
p->on("client_join_setup_completed", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string user_id = data->get_map()["user_id"]->get_string();
client_delegate* c = new client_delegate(0);
c->register_post_data_func(std::bind(&Cplanet3954webrtccplusplus::on_post_data, this, std::placeholders::_1, std::placeholders::_2));
c->connectto(); //tbd check if connected or not and apply some logic
_d_clients.insert(std::pair{ 0, c });
client_thd.insert(std::pair{ 0, new std::thread(d_client_func) });
sio::message::ptr p = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (p.get());
o->insert("user_id", current_user_id);
ngc_joined_user_socket->emit("gc_client_join_setup_completed", p);
_lock_for_join_game.unlock();
}));
p->on("ice", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string user_id = data->get_map()["user_id"]->get_string();
WebRTCIF::Ice ice;
ice.candidate = data->get_map()["candidate"]->get_string();
ice.sdp_mid = data->get_map()["sdp_mid"]->get_string();
ice.sdp_mline_index = data->get_map()["sdp_mline_index"]->get_int();
if (auto search = _webrtc.find(user_id); search != _webrtc.end()) {
WebRTCIF* tmp = search->second;
tmp->on_ice_received(ice);
}
_lock_for_join_game.unlock();
}));
p->on("sdp", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string user_id = data->get_map()["user_id"]->get_string();
std::string sdp = data->get_map()["sdp"]->get_string();
std::string typ= data->get_map()["type"]->get_string();
if (auto search = _webrtc.find(user_id); search != _webrtc.end()) {
WebRTCIF* tmp = search->second;
if (typ == "offer") {
tmp->on_create_answer_required(sdp);
}
else {
tmp->on_answered(sdp);
}
}
_lock_for_join_game.unlock();
}));
return true;
}
bool Cplanet3954webrtccplusplus::init_game_socket(){
game_socket->on("gamer_joined", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
std::string joined_game = data->get_map()["game_id"]->get_string();
if (gameid != joined_game) {
//tbd
//add error exception alike
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
return false;
}
WebRTCIF* t = createWebRTC(joined_user_id);
t->register_peer_connection_observer_cb(on_wrtc__ice_candidate, on_wrtc__conn_state_changed);
t->register_set_session_desc_observer_cb(on_wrtc__sdp_created);
t->register_error_cb(on_wrtc__error);
t->register_on_message_cb(on_wrtc__message);
t->register_on_data_channel_opent(on_wrtc__data_channel_opent);
_webrtc.insert(std::pair{joined_user_id,t});
t->init();
sio::message::ptr m = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*>(m.get());
o->insert("user_id", joined_user_id);
o->insert("game_id", joined_game);
game_socket->emit("gamer_join_initialization_ended_on_gc", m);
//tbd
//init joined_game_socket
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
game_socket->on("on_gamer_join_initialization_ended_on_gc_second_task", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
std::string joined_game = data->get_map()["game_id"]->get_string();
sio::socket::ptr s = client->socket(joined_user_id);
gc_joined_users.insert(std::pair{ "joined_user_id", s });
init_gc_joined_game_socket(s);
sio::message::ptr m = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*>(m.get());
o->insert("user_id", joined_user_id);
o->insert("game_id", joined_game);
s->emit("on_gamer_join_initialization_ended_on_gc_last_task", m);
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
return true;
}
bool Cplanet3954webrtccplusplus::init_joined_game_socket() {
//for the client to enter the main socket with game id
//client->socket(joined_user_id)
return true;
}
bool Cplanet3954webrtccplusplus::init_ngc_joined_game_socket(sio::socket::ptr p) {
//for the client to enter the userid socket
//client->socket(joined_user_id)
ngc_joined_user_socket = p;
ngc_joined_user_socket->on("gc_client_join_setup_completed", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
//tbd
// implement whatver join check it has
//end of join initialization for client side ( joined to game after this event)
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
ngc_joined_user_socket->on("on_gamer_join_initialization_ended_on_gc_last_task", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
std::string joined_game = data->get_map()["game_id"]->get_string();
WebRTCIF* t = createWebRTC(joined_user_id);
t->register_peer_connection_observer_cb(on_wrtc__ice_candidate, on_wrtc__conn_state_changed);
t->register_set_session_desc_observer_cb(on_wrtc__sdp_created);
t->register_error_cb(on_wrtc__error);
t->register_on_message_cb(on_wrtc__message);
t->register_on_data_channel_opent(on_wrtc__data_channel_opent);
_webrtc.insert(std::pair{ joined_user_id,t });
t->init();
t->create_offer();
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
ngc_joined_user_socket->on("ice", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
WebRTCIF::Ice ice;
ice.candidate = data->get_map()["candidate"]->get_string();
ice.sdp_mid = data->get_map()["sdp_mid"]->get_string();
ice.sdp_mline_index = data->get_map()["sdp_mline_index"]->get_int();
if (auto search = _webrtc.find(joined_user_id); search != _webrtc.end()) {
WebRTCIF* tmp = search->second;
tmp->on_ice_received(ice);
}
_lock_for_join_game.unlock();
}));
ngc_joined_user_socket->on("sdp", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
std::string user_id = data->get_map()["user_id"]->get_string();
std::string sdp = data->get_map()["sdp"]->get_string();
std::string typ = data->get_map()["type"]->get_string();
if (auto search = _webrtc.find(user_id); search != _webrtc.end()) {
WebRTCIF* tmp = search->second;
if (typ == "offer") {
tmp->on_create_answer_required(sdp);
}
else {
tmp->on_answered(sdp);
}
} }));
return true;
}
void Cplanet3954webrtccplusplus::on_connected()
{
_lock_for_connect_socket.lock();
_cond_for_connect_socket.notify_all();
connected = true;
current_socket = client->socket();
_lock_for_connect_socket.unlock();
if (connection_listener_for_connect) connection_listener_for_connect(STATUS_Connected, "connected");
current_socket->on("user_created", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
std::cout << "user_created received!" << std::endl;
data->print();
_lock_for_init.lock();
current_user_id = data->get_map()["user_id"]->get_string();
current_game_version = data->get_map()["game_version"]->get_string();
_is_inited = true;
_cond_for_init.notify_all();
_lock_for_init.unlock();
}));
current_socket->on("error_in_create_user", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_init.lock();
_is_inited = false;
_cond_for_init.notify_all();
_lock_for_init.unlock();
}));
current_socket->on("game_created", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_create_game.lock();
internal_created_game = data;
gameid= data->get_map()["gameid"]->get_string();
game_socket = client->socket(gameid);
init_game_socket();
//tbd
//init game_socket
_is_game_created = true;
_cond_for_create_game.notify_all();
_lock_for_create_game.unlock();
}));
current_socket->on("error_in_create_game", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_create_game.lock();
_is_game_created = false;
internal_created_game = sio::message::ptr(nullptr);
_cond_for_create_game.notify_all();
_lock_for_create_game.unlock();
}));
current_socket->on("query_games_result", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_retrieve_games.lock();
internal_retrieved_games = data;
_retrieve_games_succeeded = true;
_cond_for_retrieve_games.notify_all();
_lock_for_retrieve_games.unlock();
}));
current_socket->on("error_in_query_games", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_retrieve_games.lock();
_retrieve_games_succeeded = false;
internal_retrieved_games = sio::message::ptr(nullptr);
_cond_for_retrieve_games.notify_all();
_lock_for_retrieve_games.unlock();
}));
current_socket->on("gamer_joined", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
_join_game_succeeded = true;
joined_game_socket = client->socket(joined_gameid);
init_joined_game_socket();
std::string joined_user_id = data->get_map()["user_id"]->get_string();
std::string joined_game = data->get_map()["game_id"]->get_string();
sio::socket::ptr s = client->socket(joined_user_id);
init_ngc_joined_game_socket(s);
/*WebRTCIF* t = createWebRTC(joined_user_id);
t->register_peer_connection_observer_cb(on_wrtc__ice_candidate, on_wrtc__conn_state_changed);
t->register_set_session_desc_observer_cb(on_wrtc__sdp_created);
t->register_error_cb(on_wrtc__error);
t->register_on_message_cb(on_wrtc__message);
t->register_on_data_channel_opent(on_wrtc__data_channel_opent);
_webrtc.insert(std::pair{ joined_user_id,t });
t->init();*/
//tbd
//init joined_game_socket
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
current_socket->on("error_on_join_game", sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list& ack_resp) {
_lock_for_join_game.lock();
_join_game_succeeded = false;
_cond_for_join_game.notify_all();
_lock_for_join_game.unlock();
}));
//tbd
//initialize socket bindings
}
void init_responsed() {
}
void create_game_responsed() {
}
void join_game_responsed() {
}
void retrieve_games_responsed() {
}
bool Cplanet3954webrtccplusplus::wait_for_connection() { //sync task that asynchronously waits on caller thread with _cond
_lock_for_connect_socket.lock();
if (!connected)
{
_cond_for_connect_socket.wait(_lock_for_connect_socket);
}
_lock_for_connect_socket.unlock();
return connected;
}
void Cplanet3954webrtccplusplus::reset() {
_lock_for_connect_socket.lock();
_cond_for_connect_socket.notify_all();
connected = false;
_lock_for_connect_socket.unlock();
_lock_for_create_game.lock();
_cond_for_create_game.notify_all();
_lock_for_create_game.unlock();
_lock_for_init.lock();
_cond_for_init.notify_all();
_lock_for_init.unlock();
}
void Cplanet3954webrtccplusplus::connect() {
sio::client_options coptions = { nullptr, "C:\\Users\\guher\\planet3954_server\\keys\\CLIENT.crt", //to be converted to const buffer method from https://www.boost.org/doc/libs/latest/doc/html/boost_asio/reference/ssl__context.html
"C:\\Users\\guher\\planet3954_server\\keys\\CLIENT_key.pem", //to be converted to const buffer method and from: https://github.com/ReneNyffenegger/cpp-base64/blob/master/base64.h
"C:\\Users\\guher\\planet3954_server\\keys\\CA.crt" };
client = std::shared_ptr<sio::client>(new sio::client(coptions));
client->set_open_listener(std::bind(&Cplanet3954webrtccplusplus::on_connected, this));
client->set_close_listener(std::bind(&Cplanet3954webrtccplusplus::on_close, this, std::placeholders::_1));
client->set_fail_listener(std::bind(&Cplanet3954webrtccplusplus::on_fail, this));
client->connect("https://127.0.0.1:8443");
}
//init module
bool Cplanet3954webrtccplusplus::init(const std::string& game_version, const std::string& username, bool is_creator) {
bool tmp = false;
if (connected) {
_lock_for_init.lock();
current_user_id = username;
current_game_version = game_version;
is_creator_currently = is_creator;
_is_inited = false;
sio::message::ptr m= sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (m.get());
o->insert("is_creator", sio::bool_message::create(is_creator));
o->insert("user_id", username);
o->insert("game_version", game_version);
o->insert("score", sio::int_message::create(0));
current_socket->emit("login", m);
if (!_is_inited)
{
_cond_for_init.wait(_lock_for_init); //cond variable signalled when create init
tmp = _is_inited;
}
_lock_for_init.unlock();
return tmp;
}
else {
if (connection_listener_for_fail) connection_listener_for_fail(STATUS_Error_Generic, "init called but connection does not exist");
}
//connection_listener l(h);
}
//create game
//1. create websocket connection
//2. setup initializations
bool Cplanet3954webrtccplusplus::create_game(int session_count) {
bool tmp = false;
if (connected && _is_inited) {
_lock_for_create_game.lock();
_is_game_created = false;
if (internal_created_game.get() == nullptr) {
//tbd
//add function to ask deletion of earlier created game
}
///send message
sio::message::ptr m = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (m.get());
o->insert("user_id", current_user_id);
o->insert("game_version", current_game_version);
o->insert("capacity", sio::int_message::create(session_count));
o->insert("score", sio::int_message::create(0));
current_socket->emit("create game", m);
if (!_is_game_created)
{
_cond_for_create_game.wait(_lock_for_create_game); //cond variable signalled when create game succeeds
tmp = _is_game_created;
}
is_creator_currently = tmp;
_lock_for_create_game.unlock();
return tmp;
}
else {
if (connection_listener_for_fail) connection_listener_for_fail(STATUS_Error_Generic, "create_game called but connection or inited status does not exist");
return false;
}
}
//list games
bool Cplanet3954webrtccplusplus::retrieve_games(sio::message::ptr & games_data) {
if (connected && _is_inited) {
_lock_for_retrieve_games.lock();
bool tmp = false;
_retrieve_games_succeeded = false;
internal_retrieved_games = sio::message::ptr(nullptr);
///send message
sio::message::ptr m = sio::object_message::create();
sio::object_message* o = dynamic_cast<sio::object_message*> (m.get());
o->insert("user_id", current_user_id);
o->insert("game_version", current_game_version);
o->insert("query_len", sio::int_message::create(10));
current_socket->emit("query games", m);
std::cout << "querying with: ";
m->print();
if (!_retrieve_games_succeeded)
{
_cond_for_retrieve_games.wait(_lock_for_retrieve_games); //cond variable signalled when create game succeeds
if (_retrieve_games_succeeded)
games_data = internal_retrieved_games;
tmp = _retrieve_games_succeeded;
}
_lock_for_retrieve_games.unlock();
return tmp;
}
else {
if (connection_listener_for_fail) connection_listener_for_fail(STATUS_Error_Generic, "retrieve_games called but connection or inited status does not exist");
return false;
}
}
bool Cplanet3954webrtccplusplus::leave_game(sio::message::ptr game) {
return false;
}
bool Cplanet3954webrtccplusplus::join_game(sio::message::ptr game) {
if (connected && _is_inited) {
_lock_for_join_game.lock();
bool tmp = false;
_join_game_succeeded = false;
joined_gameid = game->get_map()["gameid"]->get_string();
//internal_retrieved_games = sio::message::ptr(nullptr);
///send message
current_socket->emit("join game", game);
if (!_join_game_succeeded)
{
_cond_for_join_game.wait(_lock_for_join_game); //cond variable signalled when create game succeeds
tmp = _join_game_succeeded;
}
_lock_for_join_game.unlock();
return tmp;
}
else {
if (connection_listener_for_fail) connection_listener_for_fail(STATUS_Error_Generic, "join game called but connection or inited status does not exist");
return false;
}
}
bool Cplanet3954webrtccplusplus::rejoin_game(sio::message::ptr game) {
return false;
}
bool Cplanet3954webrtccplusplus::bye() {
if (client.get() != nullptr) {
client->sync_close();
client->clear_con_listeners();
}
return true;
}
Yorumlar
Yorum Gönder