Refactor and fix send chat (#2864)
* feat(chat): refactor and fix send chat * fix(chat): fixes * fix(chat): fix team chat Co-authored-by: DayibBaba <79384354+DayibBaba@users.noreply.github.com>
This commit is contained in:
parent
cba19d0c33
commit
7e7bcb155a
@ -2,7 +2,7 @@
|
||||
|
||||
#include "fiber_pool.hpp"
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/chat.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -26,18 +26,7 @@ namespace big
|
||||
|
||||
void chat_command_context::report_output(const std::string& output) const
|
||||
{
|
||||
g_fiber_pool->queue_job([this, output] {
|
||||
char msg[265]{};
|
||||
msg[0] = g.session.chat_output_prefix;
|
||||
msg[1] = ' ';
|
||||
strncpy(msg + 2, output.c_str(), sizeof(msg) - 2);
|
||||
|
||||
if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_gta.m_send_chat_ptr,
|
||||
g_player_service->get_self()->get_net_data(),
|
||||
msg,
|
||||
false))
|
||||
notify::draw_chat(msg, g_player_service->get_self()->get_name(), false);
|
||||
});
|
||||
chat::send_message(output);
|
||||
}
|
||||
|
||||
void chat_command_context::report_error(const std::string& error) const
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "script.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/chat.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -36,15 +36,8 @@ namespace big
|
||||
|
||||
if (announce_in_chat)
|
||||
{
|
||||
g_fiber_pool->queue_job([attacker, victim, this] {
|
||||
auto chat = std::format("{} {}", g.session.chat_output_prefix, g_translation_service.get_translation(m_announce_message));
|
||||
|
||||
if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_gta.m_send_chat_ptr,
|
||||
g_player_service->get_self()->get_net_data(),
|
||||
chat.data(),
|
||||
is_team_only))
|
||||
notify::draw_chat(chat.c_str(), g_player_service->get_self()->get_name(), is_team_only);
|
||||
});
|
||||
auto msg = std::format("{} {}", g.session.chat_output_prefix, g_translation_service.get_translation(m_announce_message));
|
||||
chat::send_message(msg);
|
||||
}
|
||||
|
||||
if (notify)
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "pointers.hpp"
|
||||
#include "script.hpp"
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/chat.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -62,15 +62,11 @@ namespace big
|
||||
|
||||
if (announce_in_chat)
|
||||
{
|
||||
g_fiber_pool->queue_job([player, this] {
|
||||
auto chat = std::format("{} {}", g.session.chat_output_prefix, std::vformat(g_translation_service.get_translation(m_announce_message), std::make_format_args(player->get_name())));
|
||||
auto msg = std::format("{} {}",
|
||||
g.session.chat_output_prefix,
|
||||
std::vformat(g_translation_service.get_translation(m_announce_message), std::make_format_args(player->get_name())));
|
||||
|
||||
if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_gta.m_send_chat_ptr,
|
||||
g_player_service->get_self()->get_net_data(),
|
||||
chat.data(),
|
||||
is_team_only))
|
||||
notify::draw_chat(chat.c_str(), g_player_service->get_self()->get_name(), is_team_only);
|
||||
});
|
||||
chat::send_message(msg);
|
||||
}
|
||||
|
||||
if (notify)
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "packet.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "util/spam.hpp"
|
||||
#include "util/chat.hpp"
|
||||
|
||||
namespace big
|
||||
{
|
||||
@ -22,19 +22,10 @@ namespace big
|
||||
if (g.session.chat_commands && message[0] == g.session.chat_command_prefix)
|
||||
command::process(std::string(message + 1), std::make_shared<chat_command_context>(g_player_service->get_self()));
|
||||
|
||||
packet msg{};
|
||||
msg.write_message(rage::eNetMessage::MsgTextMessage);
|
||||
msg.m_buffer.WriteString(message ? message : "", 256);
|
||||
gamer_handle_serialize(g_player_service->get_self()->get_net_data()->m_gamer_handle, msg.m_buffer);
|
||||
msg.write<bool>(is_team, 1);
|
||||
chat::send_message(message, nullptr, false, is_team);
|
||||
|
||||
if (g.session.log_chat_messages)
|
||||
spam::log_chat(message, g_player_service->get_self(), SpamReason::NOT_A_SPAMMER, is_team);
|
||||
|
||||
if (*g_pointers->m_gta.m_is_session_started)
|
||||
for (auto& player : g_player_service->players())
|
||||
if (player.second && player.second->is_valid())
|
||||
msg.send(player.second->get_net_game_player()->m_msg_id);
|
||||
chat::log_chat(message, g_player_service->get_self(), SpamReason::NOT_A_SPAMMER, is_team);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "script/scriptIdBase.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include "util/spam.hpp"
|
||||
#include "util/chat.hpp"
|
||||
#include "gta/enums.hpp"
|
||||
|
||||
#include <network/Network.hpp>
|
||||
@ -114,11 +114,12 @@ namespace big
|
||||
if (player->is_spammer)
|
||||
return true;
|
||||
|
||||
if (auto spam_reason = spam::is_text_spam(message, player))
|
||||
if (auto spam_reason = chat::is_text_spam(message, player))
|
||||
{
|
||||
if (g.session.log_chat_messages)
|
||||
spam::log_chat(message, player, spam_reason, is_team);
|
||||
chat::log_chat(message, player, spam_reason, is_team);
|
||||
g_notification_service.push("PROTECTIONS"_T.data(),
|
||||
|
||||
std::format("{} {}", player->get_name(), "IS_A_SPAMMER"_T.data()));
|
||||
player->is_spammer = true;
|
||||
if (g.session.kick_chat_spammers
|
||||
@ -136,7 +137,7 @@ namespace big
|
||||
else
|
||||
{
|
||||
if (g.session.log_chat_messages)
|
||||
spam::log_chat(message, player, SpamReason::NOT_A_SPAMMER, is_team);
|
||||
chat::log_chat(message, player, SpamReason::NOT_A_SPAMMER, is_team);
|
||||
|
||||
if (g.session.chat_commands && message[0] == g.session.chat_command_prefix)
|
||||
command::process(std::string(message + 1), std::make_shared<chat_command_context>(player));
|
||||
@ -147,7 +148,6 @@ namespace big
|
||||
{
|
||||
rage::rlGamerHandle temp{};
|
||||
gamer_handle_deserialize(temp, buffer);
|
||||
bool is_team = buffer.Read<bool>(1);
|
||||
|
||||
g_pointers->m_gta.m_handle_chat_message(*g_pointers->m_gta.m_chat_data,
|
||||
nullptr,
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "pointers.hpp"
|
||||
#include "services/player_database/player_database_service.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/chat.hpp"
|
||||
#include "util/scripts.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include "util/system.hpp"
|
||||
@ -183,13 +184,22 @@ namespace lua::network
|
||||
// Sends a message to the in game chat.
|
||||
static void send_chat_message(const std::string& msg, bool team_only)
|
||||
{
|
||||
big::g_fiber_pool->queue_job([msg, team_only] {
|
||||
if (big::g_hooking->get_original<big::hooks::send_chat_message>()(*big::g_pointers->m_gta.m_send_chat_ptr,
|
||||
big::g_player_service->get_self()->get_net_data(),
|
||||
(char*)msg.c_str(),
|
||||
team_only))
|
||||
big::notify::draw_chat((char*)msg.data(), big::g_player_service->get_self()->get_name(), team_only);
|
||||
});
|
||||
big::chat::send_message(msg, nullptr, true, team_only);
|
||||
}
|
||||
|
||||
// Lua API: Function
|
||||
// Table: network
|
||||
// Name: send_chat_message_to_player
|
||||
// Param: player_idx: integer: Index of the player.
|
||||
// Param: msg: string: Message to be sent.
|
||||
// Sends a chat message to the specified player. Other players would not be able to see the message
|
||||
static void send_chat_message_to_player(int player_idx, const std::string& msg)
|
||||
{
|
||||
if (auto player = big::g_player_service->get_by_id(player_idx))
|
||||
{
|
||||
big::chat::send_message(msg, player);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void bind(sol::state& state)
|
||||
@ -229,5 +239,6 @@ namespace lua::network
|
||||
ns["get_flagged_modder_reason"] = get_flagged_modder_reason;
|
||||
ns["force_script_host"] = force_script_host;
|
||||
ns["send_chat_message"] = send_chat_message;
|
||||
ns["send_chat_message_to_player"] = send_chat_message_to_player;
|
||||
}
|
||||
}
|
240
src/util/chat.hpp
Normal file
240
src/util/chat.hpp
Normal file
@ -0,0 +1,240 @@
|
||||
#pragma once
|
||||
#include "file_manager/file.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "core/enums.hpp"
|
||||
|
||||
#include "packet.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "script.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "core/scr_globals.hpp"
|
||||
|
||||
#include <network/CNetGamePlayer.hpp>
|
||||
#include <script/HudColor.hpp>
|
||||
#include <network/ChatData.hpp>
|
||||
#include <script/globals/GPBD_FM_3.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
inline void gamer_handle_serialize(rage::rlGamerHandle& hnd, rage::datBitBuffer& buf)
|
||||
{
|
||||
constexpr int PC_PLATFORM = 3;
|
||||
buf.Write<uint8_t>(PC_PLATFORM, 8);
|
||||
buf.WriteInt64(*(int64_t*)&hnd.m_rockstar_id, 64);
|
||||
buf.Write<uint8_t>(hnd.unk_0009, 8);
|
||||
}
|
||||
|
||||
static const char* spam_texts[] =
|
||||
{
|
||||
"qq", //a chinese chat app
|
||||
"QQ",
|
||||
"WWW.",
|
||||
"www.",
|
||||
".cn",
|
||||
".CN",
|
||||
".cc",
|
||||
".CC",
|
||||
".TOP",
|
||||
".COM",
|
||||
".top",
|
||||
"\xE3\x80\x90", //left bracket in Chinese input method
|
||||
"/Menu",
|
||||
"Money/",
|
||||
"Money\\\\",
|
||||
"Money\\",
|
||||
".gg",
|
||||
"--->",
|
||||
"shopgta5",
|
||||
"doit#",
|
||||
"krutka#",
|
||||
"<b>",
|
||||
"P888",
|
||||
"gtacash",
|
||||
"\xE6\x89\xA3\xE6\x89\xA3", // no clue what this is
|
||||
"\xE5\xBE\xAE\xE4\xBF\xA1", // "wechat" in Chinese
|
||||
".cc",
|
||||
"<font s",
|
||||
"sellix.io",
|
||||
"ezcars",
|
||||
"PLANO INICIAL", // "initial plan"
|
||||
"REP +",
|
||||
"20R$", // Brazil currency?
|
||||
"l55.me",
|
||||
"\xE5\xBA\x97", //"shop" in Chinese
|
||||
"\xE9\x92\xB1", //"money" in Chinese
|
||||
"\xE5\x88\xB7", //"make(money)" in Chinese
|
||||
"\xE8\x90\x9D\xE8\x8E\x89", // "cute girl" in Chinese
|
||||
"\xE5\xA6\x88", // "mother" in Chinese
|
||||
"\xE7\xBE\x8E\xE5\xA5\xB3", // "sexy girl" in Chinese
|
||||
"\xE5\xBC\xBA\xE5\xA5\xB8", // "rape" in Chinese
|
||||
"\xE8\x90\x9D", // "loli" in Chinese
|
||||
"\xE6\x8C\x82", // "hack" in Chinese
|
||||
"\xE5\x85\x83", // chinese dollar
|
||||
"\xE9\x98\xB4\xE4\xBC\xA0\xE5\xAA\x92", // "Yin Media" in Chinese
|
||||
"\xE7\xBD\x91\xE7\xBA\xA2", // "internet celebrities" in Chinese
|
||||
"TRUSTPILOT",
|
||||
"cashlounge",
|
||||
"Fast Delivery",
|
||||
"yosativa",
|
||||
"rich2day",
|
||||
"LevelLifters",
|
||||
". com",
|
||||
"$1,000,000,000",
|
||||
"Instant Delivery",
|
||||
"0 Ban Risk",
|
||||
"Discord For Cheap Money",
|
||||
"10-30m",
|
||||
"Discord todo",
|
||||
};
|
||||
}
|
||||
|
||||
namespace big::chat
|
||||
{
|
||||
inline SpamReason is_text_spam(const char* text, player_ptr player)
|
||||
{
|
||||
if (g.session.use_spam_timer)
|
||||
{
|
||||
if (player->last_message_time.has_value())
|
||||
{
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::seconds>(currentTime - player->last_message_time.value());
|
||||
player->last_message_time.emplace(currentTime);
|
||||
|
||||
if (strlen(text) > g.session.spam_length && diff.count() <= g.session.spam_timer)
|
||||
return SpamReason::TIMER_DETECTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->last_message_time.emplace(std::chrono::steady_clock::now());
|
||||
}
|
||||
}
|
||||
for (auto e : spam_texts)
|
||||
if (strstr(text, e) != 0)
|
||||
return SpamReason::STATIC_DETECTION;
|
||||
|
||||
return SpamReason::NOT_A_SPAMMER;
|
||||
}
|
||||
|
||||
inline void log_chat(char* msg, player_ptr player, SpamReason spam_reason, bool is_team)
|
||||
{
|
||||
std::ofstream log(
|
||||
g_file_manager.get_project_file(spam_reason != SpamReason::NOT_A_SPAMMER ? "./spam.log" : "./chat.log").get_path(),
|
||||
std::ios::app);
|
||||
|
||||
auto& data = *player->get_net_data();
|
||||
auto ip = player->get_ip_address();
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
|
||||
auto timer = std::chrono::system_clock::to_time_t(now);
|
||||
auto local_time = *std::localtime(&timer);
|
||||
|
||||
std::string spam_reason_str = "";
|
||||
|
||||
switch (spam_reason)
|
||||
{
|
||||
case SpamReason::STATIC_DETECTION: spam_reason_str = "(Static Detection) "; break;
|
||||
case SpamReason::TIMER_DETECTION: spam_reason_str = "(Timer Detection) "; break;
|
||||
}
|
||||
|
||||
log << spam_reason_str << "[" << std::put_time(&local_time, "%m/%d/%Y %I:%M:%S") << ":" << std::setfill('0') << std::setw(3) << ms.count() << " " << std::put_time(&local_time, "%p") << "] ";
|
||||
|
||||
if (ip)
|
||||
log << player->get_name() << " (" << data.m_gamer_handle.m_rockstar_id << ") <" << (int)ip.value().m_field1 << "."
|
||||
<< (int)ip.value().m_field2 << "." << (int)ip.value().m_field3 << "." << (int)ip.value().m_field4 << "> " << ((is_team == true) ? "[TEAM]: " : "[ALL]: ") << msg << std::endl;
|
||||
else
|
||||
log << player->get_name() << " (" << data.m_gamer_handle.m_rockstar_id << ") <UNKNOWN> " << ((is_team == true) ? "[TEAM]: " : "[ALL]: ") << msg << std::endl;
|
||||
|
||||
log.close();
|
||||
}
|
||||
|
||||
inline void draw_chat(const char* msg, const char* player_name, bool is_team)
|
||||
{
|
||||
int scaleform = GRAPHICS::REQUEST_SCALEFORM_MOVIE("MULTIPLAYER_CHAT");
|
||||
|
||||
while (!GRAPHICS::HAS_SCALEFORM_MOVIE_LOADED(scaleform))
|
||||
script::get_current()->yield();
|
||||
|
||||
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(scaleform, "ADD_MESSAGE");
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player name
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_LITERAL_STRING(msg); // content
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_TEXTURE_NAME_STRING(HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(is_team ? "MP_CHAT_TEAM" : "MP_CHAT_ALL")); // scope
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(false); // teamOnly
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
|
||||
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
|
||||
|
||||
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(scaleform, "SET_FOCUS");
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(1); // VISIBLE_STATE_DEFAULT
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scopeType (unused)
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scope (unused)
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
|
||||
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
|
||||
|
||||
GRAPHICS::DRAW_SCALEFORM_MOVIE_FULLSCREEN(scaleform, 255, 255, 255, 255, 0);
|
||||
|
||||
// fix broken scaleforms, when chat alrdy opened
|
||||
if (const auto chat_data = *g_pointers->m_gta.m_chat_data; chat_data && (chat_data->m_chat_open || chat_data->m_timer_two))
|
||||
HUD::CLOSE_MP_TEXT_CHAT();
|
||||
}
|
||||
|
||||
inline bool is_on_same_team(CNetGamePlayer* player)
|
||||
{
|
||||
auto target_id = player->m_player_id;
|
||||
|
||||
if (NETWORK::NETWORK_IS_ACTIVITY_SESSION())
|
||||
{
|
||||
// mission
|
||||
return PLAYER::GET_PLAYER_TEAM(target_id) == PLAYER::GET_PLAYER_TEAM(self::id);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto boss_goon = &scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[self::id].BossGoon;
|
||||
|
||||
if (boss_goon->Boss == target_id)
|
||||
return true;
|
||||
|
||||
if (boss_goon->Boss == -1)
|
||||
return false;
|
||||
|
||||
if (boss_goon->Boss != self::id)
|
||||
boss_goon = &scr_globals::gpbd_fm_3.as<GPBD_FM_3*>()->Entries[boss_goon->Boss].BossGoon; // get their structure
|
||||
|
||||
// bypass some P2Cs
|
||||
for (int i = 0; i < boss_goon->Goons.Size; i++)
|
||||
{
|
||||
if (boss_goon->Goons[i] == target_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set target to send to a specific player
|
||||
inline void send_message(const std::string& message, player_ptr target = nullptr, bool draw = true, bool is_team = false)
|
||||
{
|
||||
packet msg{};
|
||||
msg.write_message(rage::eNetMessage::MsgTextMessage);
|
||||
msg.m_buffer.WriteString(message.c_str(), 256);
|
||||
gamer_handle_serialize(g_player_service->get_self()->get_net_data()->m_gamer_handle, msg.m_buffer);
|
||||
msg.write<bool>(is_team, 1);
|
||||
|
||||
if (*g_pointers->m_gta.m_is_session_started)
|
||||
for (auto& player : g_player_service->players())
|
||||
if (player.second && player.second->is_valid()
|
||||
&& (!target || target->get_net_game_player() == player.second->get_net_game_player())
|
||||
&& (!is_team || is_on_same_team(player.second->get_net_game_player())))
|
||||
msg.send(player.second->get_net_game_player()->m_msg_id);
|
||||
|
||||
if (draw)
|
||||
if (rage::tlsContext::get()->m_is_script_thread_active)
|
||||
draw_chat(message.c_str(), g_player_service->get_self()->get_name(), is_team);
|
||||
else
|
||||
g_fiber_pool->queue_job([message, target, is_team] {
|
||||
draw_chat(message.c_str(), g_player_service->get_self()->get_name(), is_team);
|
||||
});
|
||||
}
|
||||
}
|
86
src/util/notify.cpp
Normal file
86
src/util/notify.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "notify.hpp"
|
||||
#include "chat.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "gta/enums.hpp"
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "natives.hpp"
|
||||
#include "network/CNetGamePlayer.hpp"
|
||||
#include "network/ChatData.hpp"
|
||||
#include "pointers.hpp"
|
||||
#include "script.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
|
||||
namespace big::notify
|
||||
{
|
||||
void above_map(std::string_view text)
|
||||
{
|
||||
HUD::SET_TEXT_OUTLINE();
|
||||
HUD::BEGIN_TEXT_COMMAND_THEFEED_POST("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_THEFEED_POST_TICKER(false, false);
|
||||
}
|
||||
|
||||
void crash_blocked(CNetGamePlayer* player, const char* crash)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
if ((g_player_service->get_by_id(player->m_player_id)->is_friend() && g.session.trust_friends)
|
||||
|| g_player_service->get_by_id(player->m_player_id)->is_trusted || g.session.trust_session)
|
||||
return;
|
||||
|
||||
if (g.reactions.crash.notify)
|
||||
g_notification_service.push_error("Protections", std::format("Blocked {} crash from {}", crash, player->get_name()));
|
||||
|
||||
if (g.reactions.crash.log)
|
||||
LOG(WARNING) << "Blocked " << crash << " crash from " << player->get_name() << " ("
|
||||
<< (player->get_net_data() ? player->get_net_data()->m_gamer_handle.m_rockstar_id : 0) << ")";
|
||||
|
||||
if (g.reactions.crash.announce_in_chat)
|
||||
{
|
||||
auto msg = std::vformat("NOTIFICATION_CRASH_TYPE_BLOCKED"_T, std::make_format_args(player->get_name(), crash));
|
||||
msg = std::format("{} {}", g.session.chat_output_prefix, msg);
|
||||
|
||||
chat::send_message(msg);
|
||||
}
|
||||
|
||||
g.reactions.crash.process_common(g_player_service->get_by_id(player->m_player_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g.reactions.crash.notify)
|
||||
g_notification_service.push_error("Protections", std::format("Blocked {} crash from unknown player", crash));
|
||||
}
|
||||
}
|
||||
|
||||
// Shows a busy spinner till the value at the address equals the value passed or if timeout is hit
|
||||
void busy_spinner(std::string_view text, int* address, int value, int timeout)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_BUSYSPINNER_ON("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_BUSYSPINNER_ON(3);
|
||||
|
||||
for (size_t i = 0; *address != value && i < (size_t)timeout * 100; i++)
|
||||
script::get_current()->yield(10ms);
|
||||
|
||||
HUD::BUSYSPINNER_OFF();
|
||||
}
|
||||
|
||||
void show_subtitle(std::string_view text, int ms)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_PRINT("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_PRINT(ms, 1);
|
||||
}
|
||||
|
||||
void display_help_text(std::string_view text)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_DISPLAY_HELP("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_DISPLAY_HELP(0, 0, 1, -1);
|
||||
}
|
||||
|
||||
void player_joined(CNetGamePlayer* net_game_player)
|
||||
{
|
||||
above_map(std::format("<C>{}</C> joined.", net_game_player->get_name()));
|
||||
}
|
||||
}
|
@ -8,118 +8,22 @@
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "fiber_pool.hpp"
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "chat.hpp"
|
||||
|
||||
#include <script/HudColor.hpp>
|
||||
|
||||
namespace big::notify
|
||||
{
|
||||
inline void above_map(std::string_view text)
|
||||
{
|
||||
HUD::SET_TEXT_OUTLINE();
|
||||
HUD::BEGIN_TEXT_COMMAND_THEFEED_POST("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_THEFEED_POST_TICKER(false, false);
|
||||
}
|
||||
void above_map(std::string_view text);
|
||||
|
||||
inline void draw_chat(const char* msg, const char* player_name, bool is_team)
|
||||
{
|
||||
int scaleform = GRAPHICS::REQUEST_SCALEFORM_MOVIE("MULTIPLAYER_CHAT");
|
||||
|
||||
while (!GRAPHICS::HAS_SCALEFORM_MOVIE_LOADED(scaleform))
|
||||
script::get_current()->yield();
|
||||
|
||||
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(scaleform, "ADD_MESSAGE");
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player name
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_LITERAL_STRING(msg); // content
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_TEXTURE_NAME_STRING(HUD::GET_FILENAME_FOR_AUDIO_CONVERSATION(is_team ? "MP_CHAT_TEAM" : "MP_CHAT_ALL")); // scope
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_BOOL(false); // teamOnly
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
|
||||
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
|
||||
|
||||
GRAPHICS::BEGIN_SCALEFORM_MOVIE_METHOD(scaleform, "SET_FOCUS");
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(1); // VISIBLE_STATE_DEFAULT
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scopeType (unused)
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT(0); // scope (unused)
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_PLAYER_NAME_STRING(player_name); // player
|
||||
GRAPHICS::SCALEFORM_MOVIE_METHOD_ADD_PARAM_INT((int)HudColor::HUD_COLOUR_PURE_WHITE); // eHudColour
|
||||
GRAPHICS::END_SCALEFORM_MOVIE_METHOD();
|
||||
|
||||
GRAPHICS::DRAW_SCALEFORM_MOVIE_FULLSCREEN(scaleform, 255, 255, 255, 255, 0);
|
||||
|
||||
// fix broken scaleforms, when chat alrdy opened
|
||||
if (const auto chat_data = *g_pointers->m_gta.m_chat_data; chat_data && (chat_data->m_chat_open || chat_data->m_timer_two))
|
||||
HUD::CLOSE_MP_TEXT_CHAT();
|
||||
}
|
||||
|
||||
inline void crash_blocked(CNetGamePlayer* player, const char* crash)
|
||||
{
|
||||
if (player)
|
||||
{
|
||||
if ((g_player_service->get_by_id(player->m_player_id)->is_friend() && g.session.trust_friends)
|
||||
|| g_player_service->get_by_id(player->m_player_id)->is_trusted
|
||||
|| g.session.trust_session)
|
||||
return;
|
||||
|
||||
if (g.reactions.crash.notify)
|
||||
g_notification_service.push_error("Protections", std::format("Blocked {} crash from {}", crash, player->get_name()));
|
||||
|
||||
if (g.reactions.crash.log)
|
||||
LOG(WARNING) << "Blocked " << crash << " crash from " << player->get_name() << " ("
|
||||
<< (player->get_net_data() ? player->get_net_data()->m_gamer_handle.m_rockstar_id : 0) << ")";
|
||||
|
||||
if (g.reactions.crash.announce_in_chat)
|
||||
{
|
||||
g_fiber_pool->queue_job([player, crash] {
|
||||
auto chat = std::vformat("NOTIFICATION_CRASH_TYPE_BLOCKED"_T, std::make_format_args(player->get_name(), crash));
|
||||
chat = std::format("{} {}", g.session.chat_output_prefix, chat);
|
||||
|
||||
if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_gta.m_send_chat_ptr,
|
||||
g_player_service->get_self()->get_net_data(),
|
||||
chat.data(),
|
||||
g.reactions.crash.is_team_only))
|
||||
draw_chat(chat.c_str(), g_player_service->get_self()->get_name(), g.reactions.crash.is_team_only);
|
||||
});
|
||||
}
|
||||
|
||||
g.reactions.crash.process_common(g_player_service->get_by_id(player->m_player_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g.reactions.crash.notify)
|
||||
g_notification_service.push_error("Protections", std::format("Blocked {} crash from unknown player", crash));
|
||||
}
|
||||
}
|
||||
void crash_blocked(CNetGamePlayer* player, const char* crash);
|
||||
|
||||
// Shows a busy spinner till the value at the address equals the value passed or if timeout is hit
|
||||
inline void busy_spinner(std::string_view text, int* address, int value, int timeout = 15)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_BUSYSPINNER_ON("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_BUSYSPINNER_ON(3);
|
||||
void busy_spinner(std::string_view text, int* address, int value, int timeout = 15);
|
||||
|
||||
for (size_t i = 0; *address != value && i < (size_t)timeout * 100; i++)
|
||||
script::get_current()->yield(10ms);
|
||||
void show_subtitle(std::string_view text, int ms = 2000);
|
||||
|
||||
HUD::BUSYSPINNER_OFF();
|
||||
}
|
||||
|
||||
inline void show_subtitle(std::string_view text, int ms = 2000)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_PRINT("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_PRINT(ms, 1);
|
||||
}
|
||||
|
||||
inline void display_help_text(std::string_view text)
|
||||
{
|
||||
HUD::BEGIN_TEXT_COMMAND_DISPLAY_HELP("STRING");
|
||||
HUD::ADD_TEXT_COMPONENT_SUBSTRING_PLAYER_NAME(text.data());
|
||||
HUD::END_TEXT_COMMAND_DISPLAY_HELP(0, 0, 1, -1);
|
||||
}
|
||||
|
||||
inline void player_joined(CNetGamePlayer* net_game_player)
|
||||
{
|
||||
above_map(std::format("<C>{}</C> joined.", net_game_player->get_name()));
|
||||
}
|
||||
void display_help_text(std::string_view text);
|
||||
|
||||
void player_joined(CNetGamePlayer* net_game_player);
|
||||
}
|
||||
|
@ -1,128 +0,0 @@
|
||||
#pragma once
|
||||
#include "file_manager/file.hpp"
|
||||
#include "services/players/player_service.hpp"
|
||||
#include "core/enums.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
static const char* spam_texts[] = {
|
||||
"qq", //a chinese chat app
|
||||
"QQ",
|
||||
"WWW.",
|
||||
"www.",
|
||||
".cn",
|
||||
".CN",
|
||||
".cc",
|
||||
".CC",
|
||||
".TOP",
|
||||
".COM",
|
||||
".top",
|
||||
"\xE3\x80\x90", //left bracket in Chinese input method
|
||||
"/Menu",
|
||||
"Money/",
|
||||
"Money\\\\",
|
||||
"Money\\",
|
||||
".gg",
|
||||
"--->",
|
||||
"shopgta5",
|
||||
"doit#",
|
||||
"krutka#",
|
||||
"<b>",
|
||||
"P888",
|
||||
"gtacash",
|
||||
"\xE6\x89\xA3\xE6\x89\xA3", // no clue what this is
|
||||
"\xE5\xBE\xAE\xE4\xBF\xA1", // "wechat" in Chinese
|
||||
".cc",
|
||||
"<font s",
|
||||
"sellix.io",
|
||||
"ezcars",
|
||||
"PLANO INICIAL", // "initial plan"
|
||||
"REP +",
|
||||
"20R$", // Brazil currency?
|
||||
"l55.me",
|
||||
"\xE5\xBA\x97", //"shop" in Chinese
|
||||
"\xE9\x92\xB1", //"money" in Chinese
|
||||
"\xE5\x88\xB7", //"make(money)" in Chinese
|
||||
"\xE8\x90\x9D\xE8\x8E\x89", // "cute girl" in Chinese
|
||||
"\xE5\xA6\x88", // "mother" in Chinese
|
||||
"\xE7\xBE\x8E\xE5\xA5\xB3", // "sexy girl" in Chinese
|
||||
"\xE5\xBC\xBA\xE5\xA5\xB8", // "rape" in Chinese
|
||||
"\xE8\x90\x9D", // "loli" in Chinese
|
||||
"\xE6\x8C\x82", // "hack" in Chinese
|
||||
"\xE5\x85\x83", // chinese dollar
|
||||
"\xE9\x98\xB4\xE4\xBC\xA0\xE5\xAA\x92", // "Yin Media" in Chinese
|
||||
"\xE7\xBD\x91\xE7\xBA\xA2", // "internet celebrities" in Chinese
|
||||
"TRUSTPILOT",
|
||||
"cashlounge",
|
||||
"Fast Delivery",
|
||||
"yosativa",
|
||||
"rich2day",
|
||||
"LevelLifters",
|
||||
". com",
|
||||
"$1,000,000,000",
|
||||
"Instant Delivery",
|
||||
"0 Ban Risk",
|
||||
"Discord For Cheap Money",
|
||||
"10-30m",
|
||||
"Discord todo",
|
||||
};
|
||||
}
|
||||
|
||||
namespace big::spam
|
||||
{
|
||||
inline SpamReason is_text_spam(const char* text, player_ptr player)
|
||||
{
|
||||
if (g.session.use_spam_timer)
|
||||
{
|
||||
if (player->last_message_time.has_value())
|
||||
{
|
||||
auto currentTime = std::chrono::steady_clock::now();
|
||||
auto diff = std::chrono::duration_cast<std::chrono::seconds>(currentTime - player->last_message_time.value());
|
||||
player->last_message_time.emplace(currentTime);
|
||||
|
||||
if (strlen(text) > g.session.spam_length && diff.count() <= g.session.spam_timer)
|
||||
return SpamReason::TIMER_DETECTION;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->last_message_time.emplace(std::chrono::steady_clock::now());
|
||||
}
|
||||
}
|
||||
for (auto e : spam_texts)
|
||||
if (strstr(text, e) != 0)
|
||||
return SpamReason::STATIC_DETECTION;
|
||||
|
||||
return SpamReason::NOT_A_SPAMMER;
|
||||
}
|
||||
|
||||
inline void log_chat(char* msg, player_ptr player, SpamReason spam_reason, bool is_team)
|
||||
{
|
||||
std::ofstream log(g_file_manager.get_project_file(spam_reason != SpamReason::NOT_A_SPAMMER ? "./spam.log" : "./chat.log").get_path(), std::ios::app);
|
||||
|
||||
auto& data = *player->get_net_data();
|
||||
auto ip = player->get_ip_address();
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
|
||||
auto timer = std::chrono::system_clock::to_time_t(now);
|
||||
auto local_time = *std::localtime(&timer);
|
||||
|
||||
std::string spam_reason_str = "";
|
||||
|
||||
switch (spam_reason)
|
||||
{
|
||||
case SpamReason::STATIC_DETECTION: spam_reason_str = "(Static Detection) "; break;
|
||||
case SpamReason::TIMER_DETECTION: spam_reason_str = "(Timer Detection) "; break;
|
||||
}
|
||||
|
||||
log << spam_reason_str << "[" << std::put_time(&local_time, "%m/%d/%Y %I:%M:%S") << ":" << std::setfill('0') << std::setw(3) << ms.count() << " " << std::put_time(&local_time, "%p") << "] ";
|
||||
|
||||
if (ip)
|
||||
log << player->get_name() << " (" << data.m_gamer_handle.m_rockstar_id << ") <" << (int)ip.value().m_field1 << "."
|
||||
<< (int)ip.value().m_field2 << "." << (int)ip.value().m_field3 << "." << (int)ip.value().m_field4 << "> " << ((is_team == true) ? "[TEAM]: " : "[ALL]: ") << msg << std::endl;
|
||||
else
|
||||
log << player->get_name() << " (" << data.m_gamer_handle.m_rockstar_id << ") <UNKNOWN> " << ((is_team == true) ? "[TEAM]: " : "[ALL]: ") << msg << std::endl;
|
||||
|
||||
log.close();
|
||||
}
|
||||
}
|
@ -5,11 +5,11 @@
|
||||
#include "fiber_pool.hpp"
|
||||
#include "gta_util.hpp"
|
||||
#include "hooking/hooking.hpp"
|
||||
#include "util/notify.hpp"
|
||||
#include "util/chat.hpp"
|
||||
#include "util/scripts.hpp"
|
||||
#include "util/session.hpp"
|
||||
#include "util/toxic.hpp"
|
||||
#include "util/troll.hpp"
|
||||
#include "util/toxic.hpp"
|
||||
#include "views/view.hpp"
|
||||
#include "backend/bool_command.hpp"
|
||||
|
||||
@ -199,11 +199,7 @@ namespace big
|
||||
components::button("SEND"_T, [] {
|
||||
if (const auto net_game_player = gta_util::get_network_player_mgr()->m_local_net_player; net_game_player)
|
||||
{
|
||||
if (g_hooking->get_original<hooks::send_chat_message>()(*g_pointers->m_gta.m_send_chat_ptr,
|
||||
net_game_player->get_net_data(),
|
||||
msg,
|
||||
g.session.is_team))
|
||||
notify::draw_chat(msg, net_game_player->get_name(), g.session.is_team);
|
||||
chat::send_message(msg, nullptr, true, g.session.is_team);
|
||||
}
|
||||
});
|
||||
|
||||
@ -401,16 +397,16 @@ namespace big
|
||||
g_player_service->iterate([](auto& plyr) {
|
||||
toxic::start_activity(plyr.second, eActivityType::GunrunningDefend);
|
||||
});
|
||||
});
|
||||
ImGui::SeparatorText("Bounty");
|
||||
static int value = 10000;
|
||||
ImGui::SliderInt("##bountyvalue", &value, 0, 10000);
|
||||
components::command_checkbox<"anonbounty">();
|
||||
components::button("Bounty All", [] {
|
||||
g_player_service->iterate([](auto& plyr) {
|
||||
troll::set_bounty_on_player(plyr.second, value, g.session.anonymous_bounty);
|
||||
});
|
||||
});
|
||||
});
|
||||
ImGui::SeparatorText("Bounty");
|
||||
static int value = 10000;
|
||||
ImGui::SliderInt("##bountyvalue", &value, 0, 10000);
|
||||
components::command_checkbox<"anonbounty">();
|
||||
components::button("Bounty All", [] {
|
||||
g_player_service->iterate([](auto& plyr) {
|
||||
troll::set_bounty_on_player(plyr.second, value, g.session.anonymous_bounty);
|
||||
});
|
||||
});
|
||||
},
|
||||
false,
|
||||
"GRIEFING"_T.data());
|
||||
|
Reference in New Issue
Block a user