mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-13 19:49:51 +01:00
fix: avoid reward redemption crash via buffer refactor (#4949)
Co-authored-by: nerix <nero.9@hotmail.de> Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
parent
f943f70634
commit
d40b0a6c1d
|
@ -32,6 +32,7 @@
|
||||||
- Bugfix: Fixed tooltips appearing too large and/or away from the cursor. (#4920)
|
- Bugfix: Fixed tooltips appearing too large and/or away from the cursor. (#4920)
|
||||||
- Bugfix: Fixed a crash when clicking `More messages below` button in a usercard and closing it quickly. (#4933)
|
- Bugfix: Fixed a crash when clicking `More messages below` button in a usercard and closing it quickly. (#4933)
|
||||||
- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923)
|
- Bugfix: Fixed thread popup window missing messages for nested threads. (#4923)
|
||||||
|
- Bugfix: Fixed an occasional crash for channel point redemptions with text input. (#4949)
|
||||||
- Dev: Change clang-format from v14 to v16. (#4929)
|
- Dev: Change clang-format from v14 to v16. (#4929)
|
||||||
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
- Dev: Fixed UTF16 encoding of `modes` file for the installer. (#4791)
|
||||||
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
|
- Dev: Temporarily disable High DPI scaling on Qt6 builds on Windows. (#4767)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "providers/twitch/IrcMessageHandler.hpp"
|
#include "providers/twitch/IrcMessageHandler.hpp"
|
||||||
|
|
||||||
#include "Application.hpp"
|
#include "Application.hpp"
|
||||||
|
#include "common/Common.hpp"
|
||||||
#include "common/Literals.hpp"
|
#include "common/Literals.hpp"
|
||||||
#include "common/QLogging.hpp"
|
#include "common/QLogging.hpp"
|
||||||
#include "controllers/accounts/AccountController.hpp"
|
#include "controllers/accounts/AccountController.hpp"
|
||||||
|
@ -691,7 +692,7 @@ void IrcMessageHandler::handlePrivMessage(Communi::IrcPrivateMessage *message,
|
||||||
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
// https://mm2pl.github.io/emoji_rfc.pdf for more details
|
||||||
|
|
||||||
this->addMessage(
|
this->addMessage(
|
||||||
message, message->target(),
|
message, channelOrEmptyByTarget(message->target(), server),
|
||||||
message->content().replace(COMBINED_FIXER, ZERO_WIDTH_JOINER), server,
|
message->content().replace(COMBINED_FIXER, ZERO_WIDTH_JOINER), server,
|
||||||
false, message->isAction());
|
false, message->isAction());
|
||||||
|
|
||||||
|
@ -1001,7 +1002,7 @@ void IrcMessageHandler::handleUserNoticeMessage(Communi::IrcMessage *message,
|
||||||
// Messages are not required, so they might be empty
|
// Messages are not required, so they might be empty
|
||||||
if (!content.isEmpty())
|
if (!content.isEmpty())
|
||||||
{
|
{
|
||||||
this->addMessage(message, target, content, server, true, false);
|
this->addMessage(message, chn, content, server, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1260,13 +1261,11 @@ void IrcMessageHandler::setSimilarityFlags(const MessagePtr &message,
|
||||||
}
|
}
|
||||||
|
|
||||||
void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
const QString &target,
|
const ChannelPtr &chan,
|
||||||
const QString &originalContent,
|
const QString &originalContent,
|
||||||
TwitchIrcServer &server, bool isSub,
|
TwitchIrcServer &server, bool isSub,
|
||||||
bool isAction)
|
bool isAction)
|
||||||
{
|
{
|
||||||
auto chan = channelOrEmptyByTarget(target, server);
|
|
||||||
|
|
||||||
if (chan->isEmpty())
|
if (chan->isEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -1290,27 +1289,14 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
if (const auto it = tags.find("custom-reward-id"); it != tags.end())
|
if (const auto it = tags.find("custom-reward-id"); it != tags.end())
|
||||||
{
|
{
|
||||||
const auto rewardId = it.value().toString();
|
const auto rewardId = it.value().toString();
|
||||||
if (!channel->isChannelPointRewardKnown(rewardId))
|
if (!rewardId.isEmpty() &&
|
||||||
|
!channel->isChannelPointRewardKnown(rewardId))
|
||||||
{
|
{
|
||||||
// Need to wait for pubsub reward notification
|
// Need to wait for pubsub reward notification
|
||||||
auto *clone = message->clone();
|
|
||||||
qCDebug(chatterinoTwitch) << "TwitchChannel reward added ADD "
|
qCDebug(chatterinoTwitch) << "TwitchChannel reward added ADD "
|
||||||
"callback since reward is not known:"
|
"callback since reward is not known:"
|
||||||
<< rewardId;
|
<< rewardId;
|
||||||
channel->channelPointRewardAdded.connect(
|
channel->addQueuedRedemption(rewardId, originalContent, message);
|
||||||
[=, this, &server](ChannelPointReward reward) {
|
|
||||||
qCDebug(chatterinoTwitch)
|
|
||||||
<< "TwitchChannel reward added callback:" << reward.id
|
|
||||||
<< "-" << rewardId;
|
|
||||||
if (reward.id == rewardId)
|
|
||||||
{
|
|
||||||
this->addMessage(clone, target, originalContent, server,
|
|
||||||
isSub, isAction);
|
|
||||||
clone->deleteLater();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
args.channelPointRewardId = rewardId;
|
args.channelPointRewardId = rewardId;
|
||||||
|
@ -1319,7 +1305,7 @@ void IrcMessageHandler::addMessage(Communi::IrcMessage *message,
|
||||||
QString content = originalContent;
|
QString content = originalContent;
|
||||||
int messageOffset = stripLeadingReplyMention(tags, content);
|
int messageOffset = stripLeadingReplyMention(tags, content);
|
||||||
|
|
||||||
TwitchMessageBuilder builder(chan.get(), message, args, content, isAction);
|
TwitchMessageBuilder builder(channel, message, args, content, isAction);
|
||||||
builder.setMessageOffset(messageOffset);
|
builder.setMessageOffset(messageOffset);
|
||||||
|
|
||||||
if (const auto it = tags.find("reply-thread-parent-msg-id");
|
if (const auto it = tags.find("reply-thread-parent-msg-id");
|
||||||
|
|
|
@ -55,15 +55,15 @@ public:
|
||||||
void handleJoinMessage(Communi::IrcMessage *message);
|
void handleJoinMessage(Communi::IrcMessage *message);
|
||||||
void handlePartMessage(Communi::IrcMessage *message);
|
void handlePartMessage(Communi::IrcMessage *message);
|
||||||
|
|
||||||
|
void addMessage(Communi::IrcMessage *message, const ChannelPtr &chan,
|
||||||
|
const QString &originalContent, TwitchIrcServer &server,
|
||||||
|
bool isSub, bool isAction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static float similarity(const MessagePtr &msg,
|
static float similarity(const MessagePtr &msg,
|
||||||
const LimitedQueueSnapshot<MessagePtr> &messages);
|
const LimitedQueueSnapshot<MessagePtr> &messages);
|
||||||
static void setSimilarityFlags(const MessagePtr &message,
|
static void setSimilarityFlags(const MessagePtr &message,
|
||||||
const ChannelPtr &channel);
|
const ChannelPtr &channel);
|
||||||
|
|
||||||
void addMessage(Communi::IrcMessage *message, const QString &target,
|
|
||||||
const QString &originalContent, TwitchIrcServer &server,
|
|
||||||
bool isSub, bool isAction);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|
|
@ -348,6 +348,17 @@ void TwitchChannel::refreshSevenTVChannelEmotes(bool manualRefresh)
|
||||||
manualRefresh);
|
manualRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TwitchChannel::addQueuedRedemption(const QString &rewardId,
|
||||||
|
const QString &originalContent,
|
||||||
|
Communi::IrcMessage *message)
|
||||||
|
{
|
||||||
|
this->waitingRedemptions_.push_back({
|
||||||
|
rewardId,
|
||||||
|
originalContent,
|
||||||
|
{message->clone(), {}},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
|
void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
|
||||||
{
|
{
|
||||||
assertInGuiThread();
|
assertInGuiThread();
|
||||||
|
@ -368,25 +379,26 @@ void TwitchChannel::addChannelPointReward(const ChannelPointReward &reward)
|
||||||
}
|
}
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
|
const auto &channelName = this->getName();
|
||||||
qCDebug(chatterinoTwitch)
|
qCDebug(chatterinoTwitch)
|
||||||
<< "[TwitchChannel" << this->getName()
|
<< "[TwitchChannel" << channelName
|
||||||
<< "] Channel point reward added:" << reward.id << ","
|
<< "] Channel point reward added:" << reward.id << ","
|
||||||
<< reward.title << "," << reward.isUserInputRequired;
|
<< reward.title << "," << reward.isUserInputRequired;
|
||||||
|
|
||||||
// TODO: There's an underlying bug here. This bug should be fixed.
|
auto *server = getApp()->twitch;
|
||||||
// This only attempts to prevent a crash when invoking the signal.
|
auto it = std::remove_if(
|
||||||
try
|
this->waitingRedemptions_.begin(), this->waitingRedemptions_.end(),
|
||||||
{
|
[&](const QueuedRedemption &msg) {
|
||||||
this->channelPointRewardAdded.invoke(reward);
|
if (reward.id == msg.rewardID)
|
||||||
}
|
{
|
||||||
catch (const std::bad_function_call &)
|
IrcMessageHandler::instance().addMessage(
|
||||||
{
|
msg.message.get(), shared_from_this(),
|
||||||
qCWarning(chatterinoTwitch).nospace()
|
msg.originalContent, *server, false, false);
|
||||||
<< "[TwitchChannel " << this->getName()
|
return true;
|
||||||
<< "] Caught std::bad_function_call when adding channel point "
|
}
|
||||||
"reward ChannelPointReward{ id: "
|
return false;
|
||||||
<< reward.id << ", title: " << reward.title << " }.";
|
});
|
||||||
}
|
this->waitingRedemptions_.erase(it, this->waitingRedemptions_.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,15 @@
|
||||||
#include "common/Atomic.hpp"
|
#include "common/Atomic.hpp"
|
||||||
#include "common/Channel.hpp"
|
#include "common/Channel.hpp"
|
||||||
#include "common/ChannelChatters.hpp"
|
#include "common/ChannelChatters.hpp"
|
||||||
|
#include "common/Common.hpp"
|
||||||
#include "common/Outcome.hpp"
|
#include "common/Outcome.hpp"
|
||||||
#include "common/UniqueAccess.hpp"
|
#include "common/UniqueAccess.hpp"
|
||||||
#include "providers/twitch/TwitchEmotes.hpp"
|
#include "providers/twitch/TwitchEmotes.hpp"
|
||||||
#include "util/QStringHash.hpp"
|
#include "util/QStringHash.hpp"
|
||||||
|
|
||||||
|
#include <boost/circular_buffer/space_optimized.hpp>
|
||||||
#include <boost/signals2.hpp>
|
#include <boost/signals2.hpp>
|
||||||
|
#include <IrcMessage>
|
||||||
#include <pajlada/signals/signalholder.hpp>
|
#include <pajlada/signals/signalholder.hpp>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
@ -67,6 +70,8 @@ struct HelixStream;
|
||||||
|
|
||||||
class TwitchIrcServer;
|
class TwitchIrcServer;
|
||||||
|
|
||||||
|
const int MAX_QUEUED_REDEMPTIONS = 16;
|
||||||
|
|
||||||
class TwitchChannel final : public Channel, public ChannelChatters
|
class TwitchChannel final : public Channel, public ChannelChatters
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -218,8 +223,13 @@ public:
|
||||||
pajlada::Signals::NoArgSignal roomModesChanged;
|
pajlada::Signals::NoArgSignal roomModesChanged;
|
||||||
|
|
||||||
// Channel point rewards
|
// Channel point rewards
|
||||||
pajlada::Signals::SelfDisconnectingSignal<ChannelPointReward>
|
void addQueuedRedemption(const QString &rewardId,
|
||||||
channelPointRewardAdded;
|
const QString &originalContent,
|
||||||
|
Communi::IrcMessage *message);
|
||||||
|
/**
|
||||||
|
* A rich & hydrated redemption from PubSub has arrived, add it to the channel.
|
||||||
|
* This will look at queued up partial messages, and if one is found it will add the queued up partial messages fully hydrated.
|
||||||
|
**/
|
||||||
void addChannelPointReward(const ChannelPointReward &reward);
|
void addChannelPointReward(const ChannelPointReward &reward);
|
||||||
bool isChannelPointRewardKnown(const QString &rewardId);
|
bool isChannelPointRewardKnown(const QString &rewardId);
|
||||||
std::optional<ChannelPointReward> channelPointReward(
|
std::optional<ChannelPointReward> channelPointReward(
|
||||||
|
@ -246,6 +256,12 @@ private:
|
||||||
QString actualDisplayName;
|
QString actualDisplayName;
|
||||||
} nameOptions;
|
} nameOptions;
|
||||||
|
|
||||||
|
struct QueuedRedemption {
|
||||||
|
QString rewardID;
|
||||||
|
QString originalContent;
|
||||||
|
QObjectPtr<Communi::IrcMessage> message;
|
||||||
|
};
|
||||||
|
|
||||||
void refreshPubSub();
|
void refreshPubSub();
|
||||||
void refreshChatters();
|
void refreshChatters();
|
||||||
void refreshBadges();
|
void refreshBadges();
|
||||||
|
@ -356,6 +372,8 @@ private:
|
||||||
badgeSets_; // "subscribers": { "0": ... "3": ... "6": ...
|
badgeSets_; // "subscribers": { "0": ... "3": ... "6": ...
|
||||||
UniqueAccess<std::vector<CheerEmoteSet>> cheerEmoteSets_;
|
UniqueAccess<std::vector<CheerEmoteSet>> cheerEmoteSets_;
|
||||||
UniqueAccess<std::map<QString, ChannelPointReward>> channelPointRewards_;
|
UniqueAccess<std::map<QString, ChannelPointReward>> channelPointRewards_;
|
||||||
|
boost::circular_buffer_space_optimized<QueuedRedemption>
|
||||||
|
waitingRedemptions_{MAX_QUEUED_REDEMPTIONS};
|
||||||
|
|
||||||
bool mod_ = false;
|
bool mod_ = false;
|
||||||
bool vip_ = false;
|
bool vip_ = false;
|
||||||
|
|
Loading…
Reference in a new issue