Discord in game join requests
This commit is contained in:
@ -7,12 +7,25 @@
|
||||
#include "command.hpp"
|
||||
#include "network.hpp"
|
||||
#include "party.hpp"
|
||||
#include "materials.hpp"
|
||||
|
||||
#include "ui_scripting.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
#include <utils/http.hpp>
|
||||
|
||||
#include <discord_rpc.h>
|
||||
|
||||
#define DEFAULT_AVATAR "discord_default_avatar"
|
||||
#define AVATAR "discord_avatar_%s"
|
||||
|
||||
#define DEFAULT_AVATAR_URL "https://cdn.discordapp.com/embed/avatars/0.png"
|
||||
#define AVATAR_URL "https://cdn.discordapp.com/avatars/%s/%s.png?size=128"
|
||||
|
||||
#include "discord.hpp"
|
||||
|
||||
namespace discord
|
||||
{
|
||||
namespace
|
||||
@ -21,8 +34,6 @@ namespace discord
|
||||
|
||||
void update_discord()
|
||||
{
|
||||
Discord_RunCallbacks();
|
||||
|
||||
if (!game::CL_IsCgameInitialized() || game::VirtualLobby_Loaded())
|
||||
{
|
||||
discord_presence.details = game::environment::is_sp() ? "Singleplayer" : "Multiplayer";
|
||||
@ -105,6 +116,51 @@ namespace discord
|
||||
|
||||
Discord_UpdatePresence(&discord_presence);
|
||||
}
|
||||
|
||||
void download_user_avatar(const std::string& id, const std::string& avatar)
|
||||
{
|
||||
const auto data = utils::http::get_data(
|
||||
utils::string::va(AVATAR_URL, id.data(), avatar.data()));
|
||||
if (data.has_value())
|
||||
{
|
||||
materials::add(utils::string::va(AVATAR, id.data()), data.value());
|
||||
}
|
||||
}
|
||||
|
||||
bool has_default_avatar = false;
|
||||
void download_default_avatar()
|
||||
{
|
||||
const auto data = utils::http::get_data(DEFAULT_AVATAR_URL);
|
||||
if (data.has_value())
|
||||
{
|
||||
has_default_avatar = true;
|
||||
materials::add(DEFAULT_AVATAR, data.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_avatar_material(const std::string& id)
|
||||
{
|
||||
const auto avatar_name = utils::string::va(AVATAR, id.data());
|
||||
if (materials::exists(avatar_name))
|
||||
{
|
||||
return avatar_name;
|
||||
}
|
||||
|
||||
if (has_default_avatar)
|
||||
{
|
||||
return DEFAULT_AVATAR;
|
||||
}
|
||||
|
||||
return "black";
|
||||
}
|
||||
|
||||
void respond(const std::string& id, int reply)
|
||||
{
|
||||
scheduler::once([=]()
|
||||
{
|
||||
Discord_Respond(id.data(), reply);
|
||||
}, scheduler::pipeline::async);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -122,16 +178,19 @@ namespace discord
|
||||
handlers.ready = ready;
|
||||
handlers.errored = errored;
|
||||
handlers.disconnected = errored;
|
||||
handlers.joinGame = joinGame;
|
||||
handlers.joinGame = join_game;
|
||||
handlers.spectateGame = nullptr;
|
||||
handlers.joinRequest = joinRequest;
|
||||
handlers.joinRequest = join_request;
|
||||
|
||||
Discord_Initialize("947125042930667530", &handlers, 1, nullptr);
|
||||
|
||||
scheduler::once(download_default_avatar, scheduler::pipeline::async);
|
||||
|
||||
scheduler::once([]()
|
||||
{
|
||||
scheduler::once(update_discord, scheduler::pipeline::async);
|
||||
scheduler::loop(update_discord, scheduler::pipeline::async, 5s);
|
||||
scheduler::loop(Discord_RunCallbacks, scheduler::pipeline::async, 1s);
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
initialized_ = true;
|
||||
@ -153,11 +212,8 @@ namespace discord
|
||||
static void ready(const DiscordUser* request)
|
||||
{
|
||||
ZeroMemory(&discord_presence, sizeof(discord_presence));
|
||||
|
||||
discord_presence.instance = 1;
|
||||
|
||||
console::info("Discord: Ready on %s (%s)\n", request->username, request->userId);
|
||||
|
||||
Discord_UpdatePresence(&discord_presence);
|
||||
}
|
||||
|
||||
@ -166,25 +222,55 @@ namespace discord
|
||||
console::error("Discord: Error (%i): %s\n", error_code, message);
|
||||
}
|
||||
|
||||
static void joinGame(const char* joinSecret)
|
||||
static void join_game(const char* join_secret)
|
||||
{
|
||||
console::info("Discord: Join game called with join secret: %s\n", joinSecret);
|
||||
console::info("Discord: Join game called with join secret: %s\n", join_secret);
|
||||
|
||||
scheduler::once([joinSecret]()
|
||||
std::string secret = join_secret;
|
||||
scheduler::once([=]()
|
||||
{
|
||||
game::netadr_s target{};
|
||||
if (game::NET_StringToAdr(joinSecret, &target))
|
||||
if (game::NET_StringToAdr(secret.data(), &target))
|
||||
{
|
||||
console::info("Discord: Connecting to server: %s\n", joinSecret);
|
||||
console::info("Discord: Connecting to server: %s\n", secret.data());
|
||||
party::connect(target);
|
||||
}
|
||||
}, scheduler::pipeline::main);
|
||||
}
|
||||
|
||||
static void joinRequest(const DiscordUser* request)
|
||||
static void join_request(const DiscordUser* request)
|
||||
{
|
||||
console::info("Discord: joinRequest from %s (%s)\n", request->username, request->userId);
|
||||
// Discord_Respond(request->userId, DISCORD_REPLY_YES);
|
||||
console::info("Discord: join_request from %s (%s)\n", request->username, request->userId);
|
||||
|
||||
if (game::Com_InFrontend() || !ui_scripting::lui_running())
|
||||
{
|
||||
Discord_Respond(request->userId, DISCORD_REPLY_IGNORE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string user_id = request->userId;
|
||||
std::string avatar = request->avatar;
|
||||
std::string discriminator = request->discriminator;
|
||||
std::string username = request->username;
|
||||
|
||||
scheduler::once([=]()
|
||||
{
|
||||
const ui_scripting::table request_table{};
|
||||
request_table.set("avatar", avatar);
|
||||
request_table.set("discriminator", discriminator);
|
||||
request_table.set("userid", user_id);
|
||||
request_table.set("username", username);
|
||||
|
||||
ui_scripting::notify("discord_join_request",
|
||||
{
|
||||
{"request", request_table}
|
||||
});
|
||||
}, scheduler::pipeline::lui);
|
||||
|
||||
if (!materials::exists(utils::string::va(AVATAR, user_id.data())))
|
||||
{
|
||||
download_user_avatar(user_id, avatar);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
7
src/client/component/discord.hpp
Normal file
7
src/client/component/discord.hpp
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace discord
|
||||
{
|
||||
std::string get_avatar_material(const std::string& id);
|
||||
void respond(const std::string& id, int reply);
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "game_console.hpp"
|
||||
#include "ui_scripting.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
@ -15,14 +16,10 @@ namespace input
|
||||
utils::hook::detour cl_char_event_hook;
|
||||
utils::hook::detour cl_key_event_hook;
|
||||
|
||||
bool lui_running()
|
||||
{
|
||||
return *game::hks::lua_state != nullptr;
|
||||
}
|
||||
|
||||
void cl_char_event_stub(const int local_client_num, const int key)
|
||||
{
|
||||
if (lui_running())
|
||||
if (ui_scripting::lui_running())
|
||||
{
|
||||
ui_scripting::notify("keypress",
|
||||
{
|
||||
@ -41,7 +38,7 @@ namespace input
|
||||
|
||||
void cl_key_event_stub(const int local_client_num, const int key, const int down)
|
||||
{
|
||||
if (lui_running())
|
||||
if (ui_scripting::lui_running())
|
||||
{
|
||||
ui_scripting::notify(down ? "keydown" : "keyup",
|
||||
{
|
||||
|
@ -166,6 +166,14 @@ namespace materials
|
||||
});
|
||||
}
|
||||
|
||||
bool exists(const std::string& name)
|
||||
{
|
||||
return material_data.access<bool>([&](material_data_t& data_)
|
||||
{
|
||||
return data_.images.find(name) != data_.images.end();
|
||||
});
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
material_data.access([&](material_data_t& data_)
|
||||
|
@ -3,5 +3,6 @@
|
||||
namespace materials
|
||||
{
|
||||
void add(const std::string& name, const std::string& data);
|
||||
bool exists(const std::string& name);
|
||||
void clear();
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ namespace scheduler
|
||||
utils::hook::detour r_end_frame_hook;
|
||||
utils::hook::detour g_run_frame_hook;
|
||||
utils::hook::detour main_frame_hook;
|
||||
utils::hook::detour hks_frame_hook;
|
||||
|
||||
void execute(const pipeline type)
|
||||
{
|
||||
@ -112,6 +113,15 @@ namespace scheduler
|
||||
main_frame_hook.invoke<void>();
|
||||
execute(pipeline::main);
|
||||
}
|
||||
|
||||
void hks_frame_stub()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (state)
|
||||
{
|
||||
execute(pipeline::lui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||
@ -183,6 +193,7 @@ namespace scheduler
|
||||
r_end_frame_hook.create(SELECT_VALUE(0x1404F7310, 0x1405FE470), scheduler::r_end_frame_stub);
|
||||
g_run_frame_hook.create(SELECT_VALUE(0x1402772D0, 0x14033A640), scheduler::server_frame_stub);
|
||||
main_frame_hook.create(SELECT_VALUE(0x1401CE8D0, 0x1400D8310), scheduler::main_frame_stub);
|
||||
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), scheduler::hks_frame_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -16,6 +16,9 @@ namespace scheduler
|
||||
// The game's main thread
|
||||
main,
|
||||
|
||||
// LUI context
|
||||
lui,
|
||||
|
||||
count,
|
||||
};
|
||||
|
||||
|
@ -93,6 +93,7 @@ namespace ui_scripting
|
||||
|
||||
void hks_shutdown_stub()
|
||||
{
|
||||
converted_functions.clear();
|
||||
ui_scripting::lua::engine::stop();
|
||||
hks_shutdown_hook.invoke<void*>();
|
||||
}
|
||||
@ -107,15 +108,6 @@ namespace ui_scripting
|
||||
|
||||
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
|
||||
}
|
||||
|
||||
void hks_frame_stub()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (state)
|
||||
{
|
||||
ui_scripting::lua::engine::run_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main_function_handler(game::hks::lua_State* state)
|
||||
@ -132,7 +124,7 @@ namespace ui_scripting
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto function = converted_functions[closure];
|
||||
const auto& function = converted_functions[closure];
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
const auto arguments = get_return_values(count);
|
||||
const auto s = function.lua_state();
|
||||
@ -175,6 +167,11 @@ namespace ui_scripting
|
||||
error_hook_enabled = false;
|
||||
}
|
||||
|
||||
bool lui_running()
|
||||
{
|
||||
return *game::hks::lua_state != nullptr;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
@ -186,11 +183,12 @@ namespace ui_scripting
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui);
|
||||
|
||||
hks_start_hook.create(SELECT_VALUE(0x1400E4B40, 0x140176A40), hks_start_stub);
|
||||
hks_shutdown_hook.create(SELECT_VALUE(0x1400DD3D0, 0x14016CA80), hks_shutdown_stub);
|
||||
hksi_lual_error_hook.create(SELECT_VALUE(0x1400A5EA0, 0x14012F300), hksi_lual_error_stub);
|
||||
hks_allocator_hook.create(SELECT_VALUE(0x14009B570, 0x14012BAC0), hks_allocator_stub);
|
||||
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), hks_frame_stub);
|
||||
lui_error_hook.create(SELECT_VALUE(0x14007D7D0, 0x14010C9E0), lui_error_stub);
|
||||
hksi_hks_error_hook.create(SELECT_VALUE(0x14009DD80, 0x14012E390), hksi_hks_error_stub);
|
||||
|
||||
|
@ -9,4 +9,6 @@ namespace ui_scripting
|
||||
|
||||
void enable_error_hook();
|
||||
void disable_error_hook();
|
||||
|
||||
bool lui_running();
|
||||
}
|
Reference in New Issue
Block a user