diff --git a/src/Application.hpp b/src/Application.hpp index 8fda2ebba..558ae7b73 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -46,8 +46,8 @@ public: friend void test(); - [[deprecated("use getSettings() instead")]] Settings *settings = nullptr; - [[deprecated("use getPaths() instead")]] Paths *paths = nullptr; + Settings *settings = nullptr; + Paths *paths = nullptr; Theme *themes = nullptr; WindowManager *windows = nullptr; diff --git a/src/common/NetworkData.cpp b/src/common/NetworkData.cpp index 878bda409..12aec944d 100644 --- a/src/common/NetworkData.cpp +++ b/src/common/NetworkData.cpp @@ -2,12 +2,23 @@ #include "Application.hpp" #include "singletons/Paths.hpp" +#include "util/DebugCount.hpp" #include #include namespace chatterino { +NetworkData::NetworkData() +{ + DebugCount::increase("NetworkData"); +} + +NetworkData::~NetworkData() +{ + DebugCount::decrease("NetworkData"); +} + QString NetworkData::getHash() { if (this->hash_.isEmpty()) { diff --git a/src/common/NetworkData.hpp b/src/common/NetworkData.hpp index d3cecc046..d7fbf84ba 100644 --- a/src/common/NetworkData.hpp +++ b/src/common/NetworkData.hpp @@ -13,6 +13,9 @@ namespace chatterino { class NetworkResult; struct NetworkData { + NetworkData(); + ~NetworkData(); + QNetworkRequest request_; const QObject *caller_ = nullptr; bool useQuickLoadCache_{}; diff --git a/src/common/NetworkRequest.cpp b/src/common/NetworkRequest.cpp index 62929fe2a..69c636128 100644 --- a/src/common/NetworkRequest.cpp +++ b/src/common/NetworkRequest.cpp @@ -167,20 +167,21 @@ void NetworkRequest::doRequest() this->timer->start(); auto onUrlRequested = [data = this->data, timer = this->timer, worker]() mutable { - QNetworkReply *reply = nullptr; - switch (data->requestType_) { - case NetworkRequestType::Get: { - reply = NetworkManager::NaM.get(data->request_); - } break; + auto reply = [&]() -> QNetworkReply * { + switch (data->requestType_) { + case NetworkRequestType::Get: + return NetworkManager::NaM.get(data->request_); - case NetworkRequestType::Put: { - reply = NetworkManager::NaM.put(data->request_, data->payload_); - } break; + case NetworkRequestType::Put: + return NetworkManager::NaM.put(data->request_, data->payload_); - case NetworkRequestType::Delete: { - reply = NetworkManager::NaM.deleteResource(data->request_); - } break; - } + case NetworkRequestType::Delete: + return NetworkManager::NaM.deleteResource(data->request_); + + default: + return nullptr; + } + }(); if (reply == nullptr) { Log("Unhandled request type"); @@ -201,8 +202,6 @@ void NetworkRequest::doRequest() data->onReplyCreated_(reply); } - bool directAction = (data->caller_ == nullptr); - auto handleReply = [data, timer, reply]() mutable { // TODO(pajlada): A reply was received, kill the timeout timer if (reply->error() != QNetworkReply::NetworkError::NoError) { @@ -222,8 +221,7 @@ void NetworkRequest::doRequest() }; if (data->caller_ != nullptr) { - QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, - std::move(handleReply)); + QObject::connect(worker, &NetworkWorker::doneUrl, data->caller_, handleReply); QObject::connect(reply, &QNetworkReply::finished, worker, [worker]() mutable { emit worker->doneUrl(); @@ -231,7 +229,7 @@ void NetworkRequest::doRequest() }); } else { QObject::connect(reply, &QNetworkReply::finished, worker, - [handleReply = std::move(handleReply), worker]() mutable { + [handleReply, worker]() mutable { handleReply(); delete worker; diff --git a/src/controllers/commands/CommandController.cpp b/src/controllers/commands/CommandController.cpp index c558ec376..47c4e25db 100644 --- a/src/controllers/commands/CommandController.cpp +++ b/src/controllers/commands/CommandController.cpp @@ -140,7 +140,7 @@ QString CommandController::execCommand(const QString &text, ChannelPtr channel, app->twitch.server->whispersChannel->addMessage(b.getMessage()); - app->twitch.server->getWriteConnection()->sendRaw("PRIVMSG #jtv :" + text + "\r\n"); + app->twitch.server->sendMessage("jtv", text); if (getSettings()->inlineWhispers) { app->twitch.server->forEachChannel( diff --git a/src/providers/irc/AbstractIrcServer.cpp b/src/providers/irc/AbstractIrcServer.cpp index 592ce4ff1..fcbd306ae 100644 --- a/src/providers/irc/AbstractIrcServer.cpp +++ b/src/providers/irc/AbstractIrcServer.cpp @@ -35,26 +35,18 @@ AbstractIrcServer::AbstractIrcServer() // this->writeConnection->reconnectRequested.connect([this] { this->connect(); }); } -IrcConnection *AbstractIrcServer::getReadConnection() const -{ - return this->readConnection_.get(); -} - -IrcConnection *AbstractIrcServer::getWriteConnection() const -{ - return this->writeConnection_.get(); -} - void AbstractIrcServer::connect() { this->disconnect(); - // if (this->hasSeparateWriteConnection()) { - this->initializeConnection(this->writeConnection_.get(), false, true); - this->initializeConnection(this->readConnection_.get(), true, false); - // } else { - // this->initializeConnection(this->readConnection.get(), true, true); - // } + bool separateWriteConnection = this->hasSeparateWriteConnection(); + + if (separateWriteConnection) { + this->initializeConnection(this->writeConnection_.get(), false, true); + this->initializeConnection(this->readConnection_.get(), true, false); + } else { + this->initializeConnection(this->readConnection_.get(), true, true); + } // fourtf: this should be asynchronous { @@ -67,7 +59,6 @@ void AbstractIrcServer::connect() continue; } - this->writeConnection_->sendRaw("JOIN #" + chan->name); this->readConnection_->sendRaw("JOIN #" + chan->name); } @@ -88,13 +79,18 @@ void AbstractIrcServer::disconnect() } void AbstractIrcServer::sendMessage(const QString &channelName, const QString &message) +{ + this->sendRawMessage("PRIVMSG #" + channelName + " :" + message); +} + +void AbstractIrcServer::sendRawMessage(const QString &rawMessage) { std::lock_guard locker(this->connectionMutex_); - // fourtf: trim the message if it's sent from twitch chat - - if (this->writeConnection_) { - this->writeConnection_->sendRaw("PRIVMSG #" + channelName + " :" + message); + if (this->hasSeparateWriteConnection()) { + this->writeConnection_->sendRaw(rawMessage); + } else { + this->readConnection_->sendRaw(rawMessage); } } diff --git a/src/providers/irc/AbstractIrcServer.hpp b/src/providers/irc/AbstractIrcServer.hpp index 9e2723c39..734be1d87 100644 --- a/src/providers/irc/AbstractIrcServer.hpp +++ b/src/providers/irc/AbstractIrcServer.hpp @@ -17,13 +17,11 @@ public: virtual ~AbstractIrcServer() = default; // connection - IrcConnection *getReadConnection() const; - IrcConnection *getWriteConnection() const; - void connect(); void disconnect(); void sendMessage(const QString &channelName, const QString &message); + void sendRawMessage(const QString &rawMessage); // channels std::shared_ptr getOrAddChannel(const QString &dirtyChannelName); @@ -54,6 +52,7 @@ protected: virtual std::shared_ptr getCustomChannel(const QString &channelName); + virtual bool hasSeparateWriteConnection() const = 0; virtual QString cleanChannelName(const QString &dirtyChannelName); QMap> channels; diff --git a/src/providers/irc/IrcConnection2.cpp b/src/providers/irc/IrcConnection2.cpp index 82d0d1797..b1d28c20b 100644 --- a/src/providers/irc/IrcConnection2.cpp +++ b/src/providers/irc/IrcConnection2.cpp @@ -9,18 +9,23 @@ IrcConnection::IrcConnection(QObject *parent) this->pingTimer_.setInterval(5000); this->pingTimer_.start(); QObject::connect(&this->pingTimer_, &QTimer::timeout, [this] { - if (!this->recentlyReceivedMessage_.load()) { - this->sendRaw("PING"); - this->reconnectTimer_.start(); + if (this->isConnected()) { + if (!this->recentlyReceivedMessage_.load()) { + this->sendRaw("PING"); + this->reconnectTimer_.start(); + } + this->recentlyReceivedMessage_ = false; } - this->recentlyReceivedMessage_ = false; }); // reconnect after x seconds without receiving a message this->reconnectTimer_.setInterval(5000); this->reconnectTimer_.setSingleShot(true); - QObject::connect(&this->reconnectTimer_, &QTimer::timeout, - [this] { reconnectRequested.invoke(); }); + QObject::connect(&this->reconnectTimer_, &QTimer::timeout, [this] { + if (this->isConnected()) { + reconnectRequested.invoke(); + } + }); QObject::connect(this, &Communi::IrcConnection::messageReceived, [this](Communi::IrcMessage *) { this->recentlyReceivedMessage_ = true; diff --git a/src/providers/twitch/TwitchChannel.cpp b/src/providers/twitch/TwitchChannel.cpp index 97af4d729..6a77a3e89 100644 --- a/src/providers/twitch/TwitchChannel.cpp +++ b/src/providers/twitch/TwitchChannel.cpp @@ -19,7 +19,7 @@ namespace chatterino { -TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection) +TwitchChannel::TwitchChannel(const QString &channelName) : Channel(channelName, Channel::Type::Twitch) , bttvEmotes_(new EmoteMap) , ffzEmotes_(new EmoteMap) @@ -27,7 +27,6 @@ TwitchChannel::TwitchChannel(const QString &channelName, Communi::IrcConnection , channelUrl_("https://twitch.tv/" + name) , popoutPlayerUrl_("https://player.twitch.tv/?channel=" + name) , mod_(false) - , readConnection_(readConnection) { Log("[TwitchChannel:{}] Opened", this->name); @@ -420,7 +419,9 @@ bool TwitchChannel::parseRecentMessages(const QJsonObject &jsonRoot) for (const auto jsonMessage : jsonMessages) { auto content = jsonMessage.toString().toUtf8(); - auto message = Communi::IrcMessage::fromData(content, this->readConnection_); + // passing nullptr as the channel makes the message invalid but we don't check for that + // anyways + auto message = Communi::IrcMessage::fromData(content, nullptr); auto privMsg = dynamic_cast(message); assert(privMsg); diff --git a/src/providers/twitch/TwitchChannel.hpp b/src/providers/twitch/TwitchChannel.hpp index b46b59f9f..c2986029d 100644 --- a/src/providers/twitch/TwitchChannel.hpp +++ b/src/providers/twitch/TwitchChannel.hpp @@ -86,7 +86,7 @@ private: QString localizedName; }; - explicit TwitchChannel(const QString &channelName, Communi::IrcConnection *readConnection); + explicit TwitchChannel(const QString &channelName); // Methods void refreshLiveStatus(); @@ -124,7 +124,6 @@ private: QObject lifetimeGuard_; QTimer liveStatusTimer_; QTimer chattersListTimer_; - Communi::IrcConnection *readConnection_ = nullptr; friend class TwitchServer; }; diff --git a/src/providers/twitch/TwitchServer.cpp b/src/providers/twitch/TwitchServer.cpp index 39df1cc64..8649a955f 100644 --- a/src/providers/twitch/TwitchServer.cpp +++ b/src/providers/twitch/TwitchServer.cpp @@ -28,6 +28,9 @@ TwitchServer::TwitchServer() qDebug() << "init TwitchServer"; this->pubsub = new PubSub; + + getSettings()->twitchSeperateWriteConnection.connect([this](auto, auto) { this->connect(); }, + this->signalHolder_, false); } void TwitchServer::initialize(Application &app) @@ -42,12 +45,13 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, { assert(this->app); + this->singleConnection_ = isRead == isWrite; + std::shared_ptr account = getApp()->accounts->twitch.getCurrent(); qDebug() << "logging in as" << account->getUserName(); QString username = account->getUserName(); - // QString oauthClient = account->getOAuthClient(); QString oauthToken = account->getOAuthToken(); if (!oauthToken.startsWith("oauth:")) { @@ -60,9 +64,6 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, if (!account->isAnon()) { connection->setPassword(oauthToken); - - // fourtf: ignored users - // this->refreshIgnoredUsers(username, oauthClient, oauthToken); } connection->sendCommand(Communi::IrcCommand::createCapability("REQ", "twitch.tv/membership")); @@ -75,7 +76,7 @@ void TwitchServer::initializeConnection(IrcConnection *connection, bool isRead, std::shared_ptr TwitchServer::createChannel(const QString &channelName) { - TwitchChannel *channel = new TwitchChannel(channelName, this->getReadConnection()); + TwitchChannel *channel = new TwitchChannel(channelName); channel->sendMessageSignal.connect([this, channel](auto &chan, auto &msg, bool &sent) { this->onMessageSendRequested(channel, msg, sent); @@ -91,6 +92,8 @@ void TwitchServer::privateMessageReceived(Communi::IrcPrivateMessage *message) void TwitchServer::messageReceived(Communi::IrcMessage *message) { + qDebug() << message->toData(); + // this->readConnection if (message->type() == Communi::IrcMessage::Type::Private) { // We already have a handler for private messages @@ -179,6 +182,12 @@ QString TwitchServer::cleanChannelName(const QString &dirtyChannelName) return dirtyChannelName.toLower(); } +bool TwitchServer::hasSeparateWriteConnection() const +{ + return true; + // return getSettings()->twitchSeperateWriteConnection; +} + void TwitchServer::onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent) { diff --git a/src/providers/twitch/TwitchServer.hpp b/src/providers/twitch/TwitchServer.hpp index 4587d0d87..97f0859fe 100644 --- a/src/providers/twitch/TwitchServer.hpp +++ b/src/providers/twitch/TwitchServer.hpp @@ -22,7 +22,6 @@ public: virtual void initialize(Application &app) override; - // fourtf: ugh void forEachChannelAndSpecialChannels(std::function func); std::shared_ptr getChannelOrEmptyByID(const QString &channelID); @@ -36,16 +35,18 @@ public: PubSub *pubsub; protected: - void initializeConnection(IrcConnection *connection, bool isRead, bool isWrite) override; - std::shared_ptr createChannel(const QString &channelName) override; + virtual void initializeConnection(IrcConnection *connection, bool isRead, + bool isWrite) override; + virtual std::shared_ptr createChannel(const QString &channelName) override; - void privateMessageReceived(Communi::IrcPrivateMessage *message) override; - void messageReceived(Communi::IrcMessage *message) override; - void writeConnectionMessageReceived(Communi::IrcMessage *message) override; + virtual void privateMessageReceived(Communi::IrcPrivateMessage *message) override; + virtual void messageReceived(Communi::IrcMessage *message) override; + virtual void writeConnectionMessageReceived(Communi::IrcMessage *message) override; - std::shared_ptr getCustomChannel(const QString &channelname) override; + virtual std::shared_ptr getCustomChannel(const QString &channelname) override; - QString cleanChannelName(const QString &dirtyChannelName) override; + virtual QString cleanChannelName(const QString &dirtyChannelName) override; + virtual bool hasSeparateWriteConnection() const override; private: void onMessageSendRequested(TwitchChannel *channel, const QString &message, bool &sent); @@ -57,6 +58,10 @@ private: std::queue lastMessageMod_; std::chrono::steady_clock::time_point lastErrorTimeSpeed_; std::chrono::steady_clock::time_point lastErrorTimeAmount_; + + bool singleConnection_ = false; + + pajlada::Signals::SignalHolder signalHolder_; }; } // namespace chatterino diff --git a/src/singletons/Settings.hpp b/src/singletons/Settings.hpp index 1eda0e815..a785df395 100644 --- a/src/singletons/Settings.hpp +++ b/src/singletons/Settings.hpp @@ -52,6 +52,8 @@ public: BoolSetting showJoins = {"/behaviour/showJoins", false}; BoolSetting showParts = {"/behaviour/showParts", false}; FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0}; + // BoolSetting twitchSeperateWriteConnection = {"/behaviour/twitchSeperateWriteConnection", + // false}; // Auto-completion BoolSetting onlyFetchChattersForSmallerStreamers = { diff --git a/src/util/DebugCount.cpp b/src/util/DebugCount.cpp index 1f6d28a74..81be6250b 100644 --- a/src/util/DebugCount.cpp +++ b/src/util/DebugCount.cpp @@ -2,7 +2,6 @@ namespace chatterino { -QMap DebugCount::counts_; -std::mutex DebugCount::mut_; +UniqueAccess> DebugCount::counts_; } // namespace chatterino diff --git a/src/util/DebugCount.hpp b/src/util/DebugCount.hpp index 7e78641d1..46bfb83a1 100644 --- a/src/util/DebugCount.hpp +++ b/src/util/DebugCount.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -13,11 +15,11 @@ class DebugCount public: static void increase(const QString &name) { - std::lock_guard lock(mut_); + auto counts = counts_.access(); - auto it = counts_.find(name); - if (it == counts_.end()) { - counts_.insert(name, 1); + auto it = counts->find(name); + if (it == counts->end()) { + counts->insert(name, 1); } else { reinterpret_cast(it.value())++; } @@ -25,11 +27,11 @@ public: static void decrease(const QString &name) { - std::lock_guard lock(mut_); + auto counts = counts_.access(); - auto it = counts_.find(name); - if (it == counts_.end()) { - counts_.insert(name, -1); + auto it = counts->find(name); + if (it == counts->end()) { + counts->insert(name, -1); } else { reinterpret_cast(it.value())--; } @@ -37,10 +39,10 @@ public: static QString getDebugText() { - std::lock_guard lock(mut_); + auto counts = counts_.access(); QString text; - for (auto it = counts_.begin(); it != counts_.end(); it++) { + for (auto it = counts->begin(); it != counts->end(); it++) { text += it.key() + ": " + QString::number(it.value()) + "\n"; } return text; @@ -52,8 +54,7 @@ public: } private: - static QMap counts_; - static std::mutex mut_; + static UniqueAccess> counts_; }; } // namespace chatterino diff --git a/src/widgets/helper/DebugPopup.cpp b/src/widgets/helper/DebugPopup.cpp index 31162c2eb..c3bb13801 100644 --- a/src/widgets/helper/DebugPopup.cpp +++ b/src/widgets/helper/DebugPopup.cpp @@ -15,7 +15,7 @@ DebugPopup::DebugPopup() auto *text = new QLabel(this); auto *timer = new QTimer(this); - timer->setInterval(1000); + timer->setInterval(300); QObject::connect(timer, &QTimer::timeout, [text] { text->setText(DebugCount::getDebugText()); }); timer->start(); diff --git a/src/widgets/helper/DebugPopup.hpp b/src/widgets/helper/DebugPopup.hpp index b70c97d57..ec0c2bcdd 100644 --- a/src/widgets/helper/DebugPopup.hpp +++ b/src/widgets/helper/DebugPopup.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace chatterino { diff --git a/src/widgets/settingspages/FeelPage.cpp b/src/widgets/settingspages/FeelPage.cpp index 455eca9f8..83e86f2f8 100644 --- a/src/widgets/settingspages/FeelPage.cpp +++ b/src/widgets/settingspages/FeelPage.cpp @@ -24,6 +24,8 @@ FeelPage::FeelPage() auto layout = layoutCreator.setLayoutType(); + // layout.append(this->createCheckBox("Use a seperate write connection.", + // getSettings()->twitchSeperateWriteConnection)); layout.append(this->createCheckBox(SCROLL_SMOOTH, getSettings()->enableSmoothScrolling)); layout.append( this->createCheckBox(SCROLL_NEWMSG, getSettings()->enableSmoothScrollingNewMessages));