Discord rpc fixes & changes
This commit is contained in:
parent
581b60df1c
commit
409bf1c00c
@ -2,7 +2,7 @@ if (game:issingleplayer() or Engine.InFrontend()) then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local container = LUI.UIVerticalList.new({
|
local container = LUI.UIElement.new({
|
||||||
topAnchor = true,
|
topAnchor = true,
|
||||||
rightAnchor = true,
|
rightAnchor = true,
|
||||||
top = 20,
|
top = 20,
|
||||||
@ -11,18 +11,6 @@ local container = LUI.UIVerticalList.new({
|
|||||||
spacing = 5
|
spacing = 5
|
||||||
})
|
})
|
||||||
|
|
||||||
history = {}
|
|
||||||
|
|
||||||
function canasktojoin(userid)
|
|
||||||
if (history[userid] ~= nil) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
history[userid] = true
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function truncatename(name, length)
|
function truncatename(name, length)
|
||||||
if (#name <= length - 3) then
|
if (#name <= length - 3) then
|
||||||
return name
|
return name
|
||||||
@ -31,27 +19,57 @@ function truncatename(name, length)
|
|||||||
return name:sub(1, length - 3) .. "..."
|
return name:sub(1, length - 3) .. "..."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local requestlist = {}
|
||||||
|
local requestcount = 0
|
||||||
|
|
||||||
function addrequest(request)
|
function addrequest(request)
|
||||||
if (not canasktojoin(request.userid)) then
|
for i = 1, #requestlist do
|
||||||
|
if (requestlist[i].userid == request.userid or #requestlist > 5) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if (container.temp) then
|
|
||||||
container:removeElement(container.temp)
|
|
||||||
container.temp = nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
request.id = requestcount
|
||||||
|
requestcount = requestcount + 1
|
||||||
|
local yoffset = #requestlist * (75 + 5)
|
||||||
|
|
||||||
local invite = LUI.UIElement.new({
|
local invite = LUI.UIElement.new({
|
||||||
leftAnchor = true,
|
leftAnchor = true,
|
||||||
rightAnchor = true,
|
rightAnchor = true,
|
||||||
height = 75
|
height = 75,
|
||||||
|
top = yoffset
|
||||||
})
|
})
|
||||||
|
|
||||||
|
local getcurrentindex = function()
|
||||||
|
for i = 1, #requestlist do
|
||||||
|
if (requestlist[i].id == request.id) then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
invite:registerEventHandler("update_position", function()
|
||||||
|
yoffset = (getcurrentindex() - 1) * (75 + 5)
|
||||||
|
local state = {
|
||||||
|
leftAnchor = true,
|
||||||
|
height = 75,
|
||||||
|
width = 200,
|
||||||
|
left = -220,
|
||||||
|
top = yoffset
|
||||||
|
}
|
||||||
|
|
||||||
|
invite:registerAnimationState("default", state)
|
||||||
|
invite:animateToState("default", 50)
|
||||||
|
end)
|
||||||
|
|
||||||
invite:registerAnimationState("move_in", {
|
invite:registerAnimationState("move_in", {
|
||||||
leftAnchor = true,
|
leftAnchor = true,
|
||||||
height = 75,
|
height = 75,
|
||||||
width = 200,
|
width = 200,
|
||||||
left = -220
|
left = -220,
|
||||||
|
top = yoffset
|
||||||
})
|
})
|
||||||
|
|
||||||
invite:animateToState("move_in", 100)
|
invite:animateToState("move_in", 100)
|
||||||
@ -103,7 +121,7 @@ function addrequest(request)
|
|||||||
width = 32,
|
width = 32,
|
||||||
height = 32,
|
height = 32,
|
||||||
left = 1,
|
left = 1,
|
||||||
material = RegisterMaterial(avatarmaterial)
|
material = avatarmaterial
|
||||||
})
|
})
|
||||||
|
|
||||||
local username = LUI.UIText.new({
|
local username = LUI.UIText.new({
|
||||||
@ -117,8 +135,14 @@ function addrequest(request)
|
|||||||
font = CoD.TextSettings.BodyFontBold.Font
|
font = CoD.TextSettings.BodyFontBold.Font
|
||||||
})
|
})
|
||||||
|
|
||||||
username:setText(string.format("%s^7#%s requested to join your game!", truncatename(request.username, 18),
|
local requesttext = nil
|
||||||
request.discriminator))
|
if (request.discriminator == "0") then
|
||||||
|
requesttext = Engine.Localize("LUA_MENU_DISCORD_REQUEST", truncatename(request.username, 18))
|
||||||
|
else
|
||||||
|
requesttext = Engine.Localize("LUA_MENU_DISCORD_REQUEST_DISCRIMINATOR", truncatename(request.username, 18), request.discriminator)
|
||||||
|
end
|
||||||
|
|
||||||
|
username:setText(requesttext)
|
||||||
|
|
||||||
local buttons = LUI.UIElement.new({
|
local buttons = LUI.UIElement.new({
|
||||||
leftAnchor = true,
|
leftAnchor = true,
|
||||||
@ -154,57 +178,59 @@ function addrequest(request)
|
|||||||
return button
|
return button
|
||||||
end
|
end
|
||||||
|
|
||||||
buttons:addElement(createbutton("[F1] Accept", true))
|
local accepttext = Engine.Localize("LUA_MENU_DISCORD_ACCEPT", Engine.GetBinding("discord_accept"))
|
||||||
buttons:addElement(createbutton("[F2] Deny"))
|
local denytext = Engine.Localize("LUA_MENU_DISCORD_DENY", Engine.GetBinding("discord_deny"))
|
||||||
|
|
||||||
|
buttons:addElement(createbutton(accepttext, true))
|
||||||
|
buttons:addElement(createbutton(denytext))
|
||||||
|
|
||||||
local fadeouttime = 50
|
local fadeouttime = 50
|
||||||
local timeout = 10 * 1000 - fadeouttime
|
local timeout = 10 * 1000 - fadeouttime
|
||||||
|
|
||||||
local function close()
|
local function close()
|
||||||
container:processEvent({
|
table.remove(requestlist, getcurrentindex())
|
||||||
name = "update_navigation",
|
|
||||||
dispatchToChildren = true
|
|
||||||
})
|
|
||||||
invite:animateToState("fade_out", fadeouttime)
|
|
||||||
invite:addElement(LUI.UITimer.new(fadeouttime + 50, "remove"))
|
|
||||||
|
|
||||||
invite:registerEventHandler("remove", function()
|
|
||||||
container:removeElement(invite)
|
|
||||||
if (container.temp) then
|
|
||||||
container:removeElement(container.temp)
|
|
||||||
container.temp = nil
|
|
||||||
end
|
|
||||||
local temp = LUI.UIElement.new({})
|
|
||||||
container.temp = temp
|
|
||||||
container:addElement(temp)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
buttons:registerEventHandler("keydown_", function(element, event)
|
|
||||||
if (event.key == "F1") then
|
|
||||||
close()
|
|
||||||
discord.respond(request.userid, discord.reply.yes)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (event.key == "F2") then
|
|
||||||
close()
|
|
||||||
discord.respond(request.userid, discord.reply.no)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
invite:registerAnimationState("fade_out", {
|
invite:registerAnimationState("fade_out", {
|
||||||
leftAnchor = true,
|
leftAnchor = true,
|
||||||
rightAnchor = true,
|
rightAnchor = true,
|
||||||
height = 75,
|
height = 75,
|
||||||
alpha = 0,
|
alpha = 0,
|
||||||
left = 0
|
left = 0,
|
||||||
|
top = yoffset
|
||||||
})
|
})
|
||||||
|
|
||||||
|
invite:animateToState("fade_out", fadeouttime)
|
||||||
|
invite:addElement(LUI.UITimer.new(fadeouttime + 50, "remove"))
|
||||||
|
|
||||||
|
invite:registerEventHandler("remove", function()
|
||||||
|
container:removeElement(invite)
|
||||||
|
container:processEvent({
|
||||||
|
name = "update_position",
|
||||||
|
dispatchToChildren = true
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local closed = false
|
||||||
|
request.handleresponse = function(event)
|
||||||
|
if (closed) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if (event.accept) then
|
||||||
|
discord.respond(request.userid, discord.reply.yes)
|
||||||
|
else
|
||||||
|
discord.respond(request.userid, discord.reply.no)
|
||||||
|
end
|
||||||
|
|
||||||
|
closed = true
|
||||||
|
close()
|
||||||
|
end
|
||||||
|
|
||||||
invite:addElement(LUI.UITimer.new(timeout, "end_invite"))
|
invite:addElement(LUI.UITimer.new(timeout, "end_invite"))
|
||||||
invite:registerEventHandler("end_invite", function()
|
invite:registerEventHandler("end_invite", function()
|
||||||
close()
|
close()
|
||||||
discord.respond(request.userid, discord.reply.ignore)
|
discord.respond(request.userid, discord.reply.ignore)
|
||||||
history[request.userid] = nil
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local bar = LUI.UIImage.new({
|
local bar = LUI.UIImage.new({
|
||||||
@ -235,7 +261,7 @@ function addrequest(request)
|
|||||||
|
|
||||||
avatar:registerEventHandler("update", function()
|
avatar:registerEventHandler("update", function()
|
||||||
local avatarmaterial = discord.getavatarmaterial(request.userid)
|
local avatarmaterial = discord.getavatarmaterial(request.userid)
|
||||||
avatar:setImage(RegisterMaterial(avatarmaterial))
|
avatar:setImage(avatarmaterial)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
avatar:addElement(LUI.UITimer.new(100, "update"))
|
avatar:addElement(LUI.UITimer.new(100, "update"))
|
||||||
@ -249,19 +275,17 @@ function addrequest(request)
|
|||||||
padding:addElement(buttons)
|
padding:addElement(buttons)
|
||||||
|
|
||||||
container:addElement(invite)
|
container:addElement(invite)
|
||||||
|
|
||||||
|
table.insert(requestlist, request)
|
||||||
end
|
end
|
||||||
|
|
||||||
container:registerEventHandler("keydown", function(element, event)
|
LUI.roots.UIRoot0:registerEventHandler("discord_response", function(element, event)
|
||||||
local first = container:getFirstChild()
|
if (#requestlist <= 0) then
|
||||||
|
|
||||||
if (not first) then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
first:processEvent({
|
local request = requestlist[1]
|
||||||
name = "keydown_",
|
request.handleresponse(event)
|
||||||
key = event.key
|
|
||||||
})
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
LUI.roots.UIRoot0:registerEventHandler("discord_join_request", function(element, event)
|
LUI.roots.UIRoot0:registerEventHandler("discord_join_request", function(element, event)
|
||||||
|
@ -2,5 +2,10 @@
|
|||||||
"LUA_MENU_CAMPAIGN_UNLOCKED_ALL_TITLE": "Unlock All Missions and Intel",
|
"LUA_MENU_CAMPAIGN_UNLOCKED_ALL_TITLE": "Unlock All Missions and Intel",
|
||||||
"LUA_MENU_CANCEL_UNLOCK_CAPS": "Cancel Unlock All Missions",
|
"LUA_MENU_CANCEL_UNLOCK_CAPS": "Cancel Unlock All Missions",
|
||||||
"LUA_MENU_CHOOSE_LANGUAGE_DESC": "Choose your language.",
|
"LUA_MENU_CHOOSE_LANGUAGE_DESC": "Choose your language.",
|
||||||
"MENU_APPLY_LANGUAGE_SETTINGS": "Apply language settings?"
|
"MENU_APPLY_LANGUAGE_SETTINGS": "Apply language settings?",
|
||||||
|
|
||||||
|
"LUA_MENU_DISCORD_REQUEST": "&&1^7 requested to join your game!",
|
||||||
|
"LUA_MENU_DISCORD_REQUEST_DISCRIMINATOR": "&&1^7#&&2 requested to join your game!",
|
||||||
|
"LUA_MENU_DISCORD_ACCEPT": "[&&1] Accept",
|
||||||
|
"LUA_MENU_DISCORD_DENY": "[&&1] Deny"
|
||||||
}
|
}
|
@ -4,9 +4,7 @@
|
|||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
#include "discord.hpp"
|
#include "discord.hpp"
|
||||||
#include "fastfiles.hpp"
|
|
||||||
#include "materials.hpp"
|
#include "materials.hpp"
|
||||||
#include "network.hpp"
|
|
||||||
#include "party.hpp"
|
#include "party.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
@ -29,15 +27,39 @@ namespace discord
|
|||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
DiscordRichPresence discord_presence;
|
struct discord_presence_state_t
|
||||||
|
{
|
||||||
|
int start_timestamp;
|
||||||
|
int party_size;
|
||||||
|
int party_max;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct discord_presence_strings_t
|
||||||
|
{
|
||||||
|
std::string state;
|
||||||
|
std::string details;
|
||||||
|
std::string small_image_key;
|
||||||
|
std::string small_image_text;
|
||||||
|
std::string large_image_key;
|
||||||
|
std::string large_image_text;
|
||||||
|
std::string party_id;
|
||||||
|
std::string join_secret;
|
||||||
|
};
|
||||||
|
|
||||||
|
DiscordRichPresence discord_presence{};
|
||||||
|
discord_presence_strings_t discord_strings;
|
||||||
|
|
||||||
|
std::mutex avatar_map_mutex;
|
||||||
|
std::unordered_map<std::string, game::Material*> avatar_material_map;
|
||||||
|
game::Material* default_avatar_material{};
|
||||||
|
|
||||||
void update_discord_frontend()
|
void update_discord_frontend()
|
||||||
{
|
{
|
||||||
discord_presence.details = SELECT_VALUE("Singleplayer", "Multiplayer");
|
discord_presence.details = SELECT_VALUE("Singleplayer", "Multiplayer");
|
||||||
discord_presence.startTimestamp = 0;
|
discord_presence.startTimestamp = 0;
|
||||||
|
|
||||||
const auto in_firing_range = game::Dvar_FindVar("virtualLobbyInFiringRange");
|
static const auto in_firing_range = game::Dvar_FindVar("virtualLobbyInFiringRange");
|
||||||
if (in_firing_range && in_firing_range->current.enabled == 1)
|
if (in_firing_range != nullptr && in_firing_range->current.enabled == 1)
|
||||||
{
|
{
|
||||||
discord_presence.state = "Firing Range";
|
discord_presence.state = "Firing Range";
|
||||||
discord_presence.largeImageKey = "mp_vlobby_room";
|
discord_presence.largeImageKey = "mp_vlobby_room";
|
||||||
@ -56,7 +78,7 @@ namespace discord
|
|||||||
static const auto mapname_dvar = game::Dvar_FindVar("mapname");
|
static const auto mapname_dvar = game::Dvar_FindVar("mapname");
|
||||||
auto mapname = mapname_dvar->current.string;
|
auto mapname = mapname_dvar->current.string;
|
||||||
|
|
||||||
discord_presence.largeImageKey = mapname;
|
discord_strings.large_image_key = mapname;
|
||||||
|
|
||||||
const auto presence_key = utils::string::va("PRESENCE_%s%s", SELECT_VALUE("SP_", ""), mapname);
|
const auto presence_key = utils::string::va("PRESENCE_%s%s", SELECT_VALUE("SP_", ""), mapname);
|
||||||
if (game::DB_XAssetExists(game::ASSET_TYPE_LOCALIZE, presence_key) &&
|
if (game::DB_XAssetExists(game::ASSET_TYPE_LOCALIZE, presence_key) &&
|
||||||
@ -67,17 +89,15 @@ namespace discord
|
|||||||
|
|
||||||
if (game::environment::is_mp())
|
if (game::environment::is_mp())
|
||||||
{
|
{
|
||||||
// setup Discord details (Free-for-all on Shipment)
|
|
||||||
static char gametype[0x80] = {0};
|
|
||||||
static const auto gametype_dvar = game::Dvar_FindVar("g_gametype");
|
static const auto gametype_dvar = game::Dvar_FindVar("g_gametype");
|
||||||
|
static const auto max_clients_dvar = game::Dvar_FindVar("sv_maxclients");
|
||||||
|
static const auto hostname_dvar = game::Dvar_FindVar("sv_hostname");
|
||||||
|
|
||||||
const auto gametype_display_name = game::UI_GetGameTypeDisplayName(gametype_dvar->current.string);
|
const auto gametype_display_name = game::UI_GetGameTypeDisplayName(gametype_dvar->current.string);
|
||||||
utils::string::strip(gametype_display_name, gametype, sizeof(gametype));
|
const auto gametype = utils::string::strip(gametype_display_name);
|
||||||
|
|
||||||
static char details[0x80] = {0};
|
discord_strings.details = std::format("{} on {}", gametype, mapname);
|
||||||
strcpy_s(details, 0x80, utils::string::va("%s on %s", gametype, mapname));
|
|
||||||
discord_presence.details = details;
|
|
||||||
|
|
||||||
// setup Discord party (1 of 18)
|
|
||||||
const auto client_state = *game::mp::client_state;
|
const auto client_state = *game::mp::client_state;
|
||||||
if (client_state != nullptr)
|
if (client_state != nullptr)
|
||||||
{
|
{
|
||||||
@ -86,21 +106,16 @@ namespace discord
|
|||||||
|
|
||||||
if (game::SV_Loaded())
|
if (game::SV_Loaded())
|
||||||
{
|
{
|
||||||
discord_presence.state = "Private Match";
|
discord_strings.state = "Private Match";
|
||||||
static const auto maxclients_dvar = game::Dvar_FindVar("sv_maxclients");
|
discord_presence.partyMax = max_clients_dvar->current.integer;
|
||||||
discord_presence.partyMax = maxclients_dvar->current.integer;
|
|
||||||
discord_presence.partyPrivacy = DISCORD_PARTY_PRIVATE;
|
discord_presence.partyPrivacy = DISCORD_PARTY_PRIVATE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static char hostname[0x80] = {0};
|
discord_strings.state = utils::string::strip(hostname_dvar->current.string);
|
||||||
static const auto hostname_dvar = game::Dvar_FindVar("sv_hostname");
|
|
||||||
utils::string::strip(hostname_dvar->current.string, hostname, sizeof(hostname));
|
|
||||||
discord_presence.state = hostname;
|
|
||||||
|
|
||||||
const auto server_connection_state = party::get_server_connection_state();
|
const auto server_connection_state = party::get_server_connection_state();
|
||||||
|
const auto server_ip_port = std::format("{}.{}.{}.{}:{}",
|
||||||
const auto server_ip_port = utils::string::va("%i.%i.%i.%i:%i",
|
|
||||||
static_cast<int>(server_connection_state.host.ip[0]),
|
static_cast<int>(server_connection_state.host.ip[0]),
|
||||||
static_cast<int>(server_connection_state.host.ip[1]),
|
static_cast<int>(server_connection_state.host.ip[1]),
|
||||||
static_cast<int>(server_connection_state.host.ip[2]),
|
static_cast<int>(server_connection_state.host.ip[2]),
|
||||||
@ -108,28 +123,22 @@ namespace discord
|
|||||||
static_cast<int>(ntohs(server_connection_state.host.port))
|
static_cast<int>(ntohs(server_connection_state.host.port))
|
||||||
);
|
);
|
||||||
|
|
||||||
static char party_id[0x80] = {0};
|
discord_strings.party_id = utils::cryptography::sha1::compute(server_ip_port, true).substr(0, 8);
|
||||||
const auto server_ip_port_hash = utils::cryptography::sha1::compute(server_ip_port, true).substr(0, 8);
|
|
||||||
strcpy_s(party_id, 0x80, server_ip_port_hash.data());
|
|
||||||
discord_presence.partyId = party_id;
|
|
||||||
discord_presence.partyMax = server_connection_state.max_clients;
|
discord_presence.partyMax = server_connection_state.max_clients;
|
||||||
discord_presence.partyPrivacy = DISCORD_PARTY_PUBLIC;
|
discord_presence.partyPrivacy = DISCORD_PARTY_PUBLIC;
|
||||||
|
discord_strings.join_secret = server_ip_port;
|
||||||
static char join_secret[0x80] = { 0 };
|
|
||||||
strcpy_s(join_secret, 0x80, server_ip_port);
|
|
||||||
discord_presence.joinSecret = join_secret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto server_discord_information = party::get_server_discord_information();
|
auto server_discord_info = party::get_server_discord_info();
|
||||||
if (!server_discord_information.image.empty())
|
if (server_discord_info.has_value())
|
||||||
{
|
{
|
||||||
discord_presence.smallImageKey = server_discord_information.image.data();
|
discord_strings.small_image_key = server_discord_info->image;
|
||||||
discord_presence.smallImageText = server_discord_information.image_text.data();
|
discord_strings.small_image_text = server_discord_info->image_text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (game::environment::is_sp())
|
else if (game::environment::is_sp())
|
||||||
{
|
{
|
||||||
discord_presence.details = mapname;
|
discord_strings.details = mapname;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discord_presence.startTimestamp == 0)
|
if (discord_presence.startTimestamp == 0)
|
||||||
@ -138,12 +147,20 @@ namespace discord
|
|||||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
discord_presence.state = discord_strings.state.data();
|
||||||
|
discord_presence.details = discord_strings.details.data();
|
||||||
|
discord_presence.smallImageKey = discord_strings.small_image_key.data();
|
||||||
|
discord_presence.smallImageText = discord_strings.small_image_text.data();
|
||||||
|
discord_presence.largeImageKey = discord_strings.large_image_key.data();
|
||||||
|
discord_presence.largeImageText = discord_strings.large_image_text.data();
|
||||||
|
discord_presence.partyId = discord_strings.party_id.data();
|
||||||
|
discord_presence.joinSecret = discord_strings.join_secret.data();
|
||||||
|
|
||||||
Discord_UpdatePresence(&discord_presence);
|
Discord_UpdatePresence(&discord_presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_discord()
|
void update_discord()
|
||||||
{
|
{
|
||||||
// reset presence data
|
|
||||||
const auto saved_time = discord_presence.startTimestamp;
|
const auto saved_time = discord_presence.startTimestamp;
|
||||||
discord_presence = {};
|
discord_presence = {};
|
||||||
discord_presence.startTimestamp = saved_time;
|
discord_presence.startTimestamp = saved_time;
|
||||||
@ -158,6 +175,33 @@ namespace discord
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game::Material* create_avatar_material(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
const auto material = materials::create_material(name);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!materials::setup_material_image(material, data))
|
||||||
|
{
|
||||||
|
materials::free_material(material);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard _0(avatar_map_mutex);
|
||||||
|
avatar_material_map.insert(std::make_pair(name, material));
|
||||||
|
}
|
||||||
|
|
||||||
|
return material;
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
materials::free_material(material);
|
||||||
|
console::error("Failed to load user avatar image: %s\n", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void download_user_avatar(const std::string& id, const std::string& avatar)
|
void download_user_avatar(const std::string& id, const std::string& avatar)
|
||||||
{
|
{
|
||||||
const auto data = utils::http::get_data(
|
const auto data = utils::http::get_data(
|
||||||
@ -173,10 +217,10 @@ namespace discord
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
materials::add(utils::string::va(AVATAR, id.data()), value.buffer);
|
const auto name = utils::string::va(AVATAR, id.data());
|
||||||
|
create_avatar_material(name, value.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_default_avatar = false;
|
|
||||||
void download_default_avatar()
|
void download_default_avatar()
|
||||||
{
|
{
|
||||||
const auto data = utils::http::get_data(DEFAULT_AVATAR_URL);
|
const auto data = utils::http::get_data(DEFAULT_AVATAR_URL);
|
||||||
@ -191,8 +235,7 @@ namespace discord
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
has_default_avatar = true;
|
default_avatar_material = create_avatar_material(DEFAULT_AVATAR, value.buffer);
|
||||||
materials::add(DEFAULT_AVATAR, value.buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ready(const DiscordUser* request)
|
void ready(const DiscordUser* request)
|
||||||
@ -211,6 +254,8 @@ namespace discord
|
|||||||
|
|
||||||
void join_game(const char* join_secret)
|
void join_game(const char* join_secret)
|
||||||
{
|
{
|
||||||
|
console::debug("Discord: join_game called with secret '%s'\n", join_secret);
|
||||||
|
|
||||||
scheduler::once([=]
|
scheduler::once([=]
|
||||||
{
|
{
|
||||||
game::netadr_s target{};
|
game::netadr_s target{};
|
||||||
@ -232,10 +277,30 @@ namespace discord
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string user_id = request->userId;
|
static std::unordered_map<std::string, std::chrono::high_resolution_clock::time_point> last_requests;
|
||||||
std::string avatar = request->avatar;
|
|
||||||
std::string discriminator = request->discriminator;
|
const std::string user_id = request->userId;
|
||||||
std::string username = request->username;
|
const std::string avatar = request->avatar;
|
||||||
|
const std::string discriminator = request->discriminator;
|
||||||
|
const std::string username = request->username;
|
||||||
|
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto iter = last_requests.find(user_id);
|
||||||
|
if (iter != last_requests.end())
|
||||||
|
{
|
||||||
|
if ((now - iter->second) < 15s)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iter->second = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
last_requests.insert(std::make_pair(user_id, now));
|
||||||
|
}
|
||||||
|
|
||||||
scheduler::once([=]
|
scheduler::once([=]
|
||||||
{
|
{
|
||||||
@ -251,27 +316,47 @@ namespace discord
|
|||||||
});
|
});
|
||||||
}, scheduler::pipeline::lui);
|
}, scheduler::pipeline::lui);
|
||||||
|
|
||||||
if (!materials::exists(utils::string::va(AVATAR, user_id.data())))
|
const auto material_name = utils::string::va(AVATAR, user_id.data());
|
||||||
|
if (!avatar.empty() && !avatar_material_map.contains(material_name))
|
||||||
{
|
{
|
||||||
download_user_avatar(user_id, avatar);
|
download_user_avatar(user_id, avatar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_default_bindings()
|
||||||
|
{
|
||||||
|
const auto set_binding = [](const std::string& command, const game::keyNum_t key)
|
||||||
|
{
|
||||||
|
const auto binding = game::Key_GetBindingForCmd(command.data());
|
||||||
|
for (auto i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
if (game::playerKeys[0].keys[i].binding == binding)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_avatar_material(const std::string& id)
|
if (game::playerKeys[0].keys[key].binding == 0)
|
||||||
{
|
{
|
||||||
const auto avatar_name = utils::string::va(AVATAR, id.data());
|
game::Key_SetBinding(0, key, binding);
|
||||||
if (materials::exists(avatar_name))
|
}
|
||||||
{
|
};
|
||||||
return avatar_name;
|
|
||||||
|
set_binding("discord_accept", game::K_F1);
|
||||||
|
set_binding("discord_deny", game::K_F2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_default_avatar)
|
game::Material* get_avatar_material(const std::string& id)
|
||||||
{
|
{
|
||||||
return DEFAULT_AVATAR;
|
const auto material_name = utils::string::va(AVATAR, id.data());
|
||||||
|
const auto iter = avatar_material_map.find(material_name);
|
||||||
|
if (iter == avatar_material_map.end())
|
||||||
|
{
|
||||||
|
return default_avatar_material;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "black";
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void respond(const std::string& id, int reply)
|
void respond(const std::string& id, int reply)
|
||||||
@ -311,16 +396,29 @@ namespace discord
|
|||||||
|
|
||||||
Discord_Initialize("947125042930667530", &handlers, 1, nullptr);
|
Discord_Initialize("947125042930667530", &handlers, 1, nullptr);
|
||||||
|
|
||||||
scheduler::once(download_default_avatar, scheduler::pipeline::async);
|
if (game::environment::is_mp())
|
||||||
|
|
||||||
scheduler::once([]()
|
|
||||||
{
|
{
|
||||||
scheduler::once(update_discord, scheduler::pipeline::async);
|
scheduler::on_game_initialized([]
|
||||||
scheduler::loop(update_discord, scheduler::pipeline::async, 5s);
|
{
|
||||||
scheduler::loop(Discord_RunCallbacks, scheduler::pipeline::async, 1s);
|
scheduler::once(download_default_avatar, scheduler::async);
|
||||||
}, scheduler::pipeline::main);
|
set_default_bindings();
|
||||||
|
}, scheduler::main);
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler::loop(Discord_RunCallbacks, scheduler::async, 500ms);
|
||||||
|
scheduler::loop(update_discord, scheduler::async, 5s);
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
|
|
||||||
|
command::add("discord_accept", []()
|
||||||
|
{
|
||||||
|
ui_scripting::notify("discord_response", {{"accept", true}});
|
||||||
|
});
|
||||||
|
|
||||||
|
command::add("discord_deny", []()
|
||||||
|
{
|
||||||
|
ui_scripting::notify("discord_response", {{"accept", false}});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void pre_destroy() override
|
void pre_destroy() override
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
namespace discord
|
namespace discord
|
||||||
{
|
{
|
||||||
std::string get_avatar_material(const std::string& id);
|
game::Material* get_avatar_material(const std::string& id);
|
||||||
void respond(const std::string& id, int reply);
|
void respond(const std::string& id, int reply);
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,6 @@ namespace input
|
|||||||
|
|
||||||
void cl_char_event_stub(const int local_client_num, const int key)
|
void cl_char_event_stub(const int local_client_num, const int key)
|
||||||
{
|
{
|
||||||
if (game::environment::is_sp() && ui_scripting::lui_running())
|
|
||||||
{
|
|
||||||
ui_scripting::notify("keypress",
|
|
||||||
{
|
|
||||||
{"keynum", key},
|
|
||||||
{"key", game::Key_KeynumToString(key, 0, 1)},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game_console::console_char_event(local_client_num, key))
|
if (!game_console::console_char_event(local_client_num, key))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -36,15 +27,6 @@ namespace input
|
|||||||
|
|
||||||
void cl_key_event_stub(const int local_client_num, const int key, const int down)
|
void cl_key_event_stub(const int local_client_num, const int key, const int down)
|
||||||
{
|
{
|
||||||
if (ui_scripting::lui_running())
|
|
||||||
{
|
|
||||||
ui_scripting::notify(down ? "keydown" : "keyup",
|
|
||||||
{
|
|
||||||
{"keynum", key},
|
|
||||||
{"key", game::Key_KeynumToString(key, 0, 1)},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game_console::console_key_event(local_client_num, key, down))
|
if (!game_console::console_key_event(local_client_num, key, down))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -21,7 +21,6 @@ namespace materials
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
utils::hook::detour db_material_streaming_fail_hook;
|
utils::hook::detour db_material_streaming_fail_hook;
|
||||||
utils::hook::detour material_register_handle_hook;
|
|
||||||
utils::hook::detour db_get_material_index_hook;
|
utils::hook::detour db_get_material_index_hook;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -31,120 +30,8 @@ namespace materials
|
|||||||
const game::dvar_t* debug_materials = nullptr;
|
const game::dvar_t* debug_materials = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct material_data_t
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, game::Material*> materials;
|
|
||||||
std::unordered_map<std::string, std::string> images;
|
|
||||||
};
|
|
||||||
|
|
||||||
char constant_table[0x20] = {};
|
char constant_table[0x20] = {};
|
||||||
|
|
||||||
utils::concurrency::container<material_data_t> material_data;
|
|
||||||
|
|
||||||
game::GfxImage* setup_image(game::GfxImage* image, const utils::image& raw_image)
|
|
||||||
{
|
|
||||||
image->imageFormat = 0x1000003;
|
|
||||||
image->resourceSize = -1;
|
|
||||||
|
|
||||||
D3D11_SUBRESOURCE_DATA data{};
|
|
||||||
data.SysMemPitch = raw_image.get_width() * 4;
|
|
||||||
data.SysMemSlicePitch = data.SysMemPitch * raw_image.get_height();
|
|
||||||
data.pSysMem = raw_image.get_buffer();
|
|
||||||
|
|
||||||
game::Image_Setup(image, raw_image.get_width(), raw_image.get_height(), image->depth, image->numElements,
|
|
||||||
image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, image->name, &data);
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Material* create_material(const std::string& name, const std::string& data)
|
|
||||||
{
|
|
||||||
const auto white = material_register_handle_hook.invoke<game::Material*>("white");
|
|
||||||
const auto material = utils::memory::get_allocator()->allocate<game::Material>();
|
|
||||||
const auto texture_table = utils::memory::get_allocator()->allocate<game::MaterialTextureDef>();
|
|
||||||
const auto image = utils::memory::get_allocator()->allocate<game::GfxImage>();
|
|
||||||
|
|
||||||
std::memcpy(material, white, sizeof(game::Material));
|
|
||||||
std::memcpy(texture_table, white->textureTable, sizeof(game::MaterialTextureDef));
|
|
||||||
std::memcpy(image, white->textureTable->u.image, sizeof(game::GfxImage));
|
|
||||||
|
|
||||||
material->constantTable = &constant_table;
|
|
||||||
material->name = utils::memory::get_allocator()->duplicate_string(name);
|
|
||||||
image->name = material->name;
|
|
||||||
|
|
||||||
material->textureTable = texture_table;
|
|
||||||
material->textureTable->u.image = setup_image(image, data);
|
|
||||||
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_material(game::Material* material)
|
|
||||||
{
|
|
||||||
material->textureTable->u.image->textures.___u0.map->Release();
|
|
||||||
material->textureTable->u.image->textures.shaderView->Release();
|
|
||||||
utils::memory::get_allocator()->free(material->textureTable->u.image);
|
|
||||||
utils::memory::get_allocator()->free(material->textureTable);
|
|
||||||
utils::memory::get_allocator()->free(material->name);
|
|
||||||
utils::memory::get_allocator()->free(material);
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Material* load_material(const std::string& name)
|
|
||||||
{
|
|
||||||
return material_data.access<game::Material*>([&](material_data_t& data_) -> game::Material*
|
|
||||||
{
|
|
||||||
if (const auto i = data_.materials.find(name); i != data_.materials.end())
|
|
||||||
{
|
|
||||||
return i->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string data{};
|
|
||||||
if (const auto i = data_.images.find(name); i != data_.images.end())
|
|
||||||
{
|
|
||||||
data = i->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
|
|
||||||
{
|
|
||||||
data_.materials[name] = nullptr;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto material = create_material(name, data);
|
|
||||||
data_.materials[name] = material;
|
|
||||||
|
|
||||||
return material;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Material* try_load_material(const std::string& name)
|
|
||||||
{
|
|
||||||
if (name == "white")
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return load_material(name);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
console::error("Failed to load material %s: %s\n", name.data(), e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Material* material_register_handle_stub(const char* name)
|
|
||||||
{
|
|
||||||
auto result = try_load_material(name);
|
|
||||||
if (result == nullptr)
|
|
||||||
{
|
|
||||||
result = material_register_handle_hook.invoke<game::Material*>(name);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int db_material_streaming_fail_stub(game::Material* material)
|
int db_material_streaming_fail_stub(game::Material* material)
|
||||||
{
|
{
|
||||||
if (material->constantTable == &constant_table)
|
if (material->constantTable == &constant_table)
|
||||||
@ -238,38 +125,73 @@ namespace materials
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(const std::string& name, const std::string& data)
|
bool setup_material_image(game::Material* material, const std::string& data)
|
||||||
{
|
{
|
||||||
material_data.access([&](material_data_t& data_)
|
if (*game::d3d11_device == nullptr)
|
||||||
{
|
{
|
||||||
data_.images[name] = data;
|
console::error("Tried to create texture while d3d11 device isn't initialized\n");
|
||||||
});
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool exists(const std::string& name)
|
const auto image = material->textureTable->u.image;
|
||||||
{
|
image->imageFormat = 0x1000003;
|
||||||
return material_data.access<bool>([&](material_data_t& data_)
|
image->resourceSize = -1;
|
||||||
{
|
|
||||||
return data_.images.find(name) != data_.images.end();
|
auto raw_image = utils::image{data};
|
||||||
});
|
|
||||||
|
D3D11_SUBRESOURCE_DATA resource_data{};
|
||||||
|
resource_data.SysMemPitch = raw_image.get_width() * 4;
|
||||||
|
resource_data.SysMemSlicePitch = resource_data.SysMemPitch * raw_image.get_height();
|
||||||
|
resource_data.pSysMem = raw_image.get_buffer();
|
||||||
|
|
||||||
|
game::Image_Setup(image, raw_image.get_width(), raw_image.get_height(), image->depth, image->numElements,
|
||||||
|
image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, image->name, &resource_data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
game::Material* create_material(const std::string& name)
|
||||||
{
|
{
|
||||||
material_data.access([&](material_data_t& data_)
|
const auto white = game::Material_RegisterHandle("$white");
|
||||||
{
|
const auto material = utils::memory::allocate<game::Material>();
|
||||||
for (auto& material : data_.materials)
|
const auto texture_table = utils::memory::allocate<game::MaterialTextureDef>();
|
||||||
{
|
const auto image = utils::memory::allocate<game::GfxImage>();
|
||||||
if (material.second == nullptr)
|
|
||||||
{
|
std::memcpy(material, white, sizeof(game::Material));
|
||||||
continue;
|
std::memcpy(texture_table, white->textureTable, sizeof(game::MaterialTextureDef));
|
||||||
|
std::memcpy(image, white->textureTable->u.image, sizeof(game::GfxImage));
|
||||||
|
|
||||||
|
material->constantTable = &constant_table;
|
||||||
|
material->name = utils::memory::duplicate_string(name);
|
||||||
|
image->name = material->name;
|
||||||
|
|
||||||
|
image->textures.map = nullptr;
|
||||||
|
image->textures.shaderView = nullptr;
|
||||||
|
image->textures.shaderViewAlternate = nullptr;
|
||||||
|
|
||||||
|
material->textureTable = texture_table;
|
||||||
|
|
||||||
|
return material;
|
||||||
}
|
}
|
||||||
|
|
||||||
free_material(material.second);
|
void free_material(game::Material* material)
|
||||||
|
{
|
||||||
|
const auto try_release = []<typename T>(T** resource)
|
||||||
|
{
|
||||||
|
if (*resource != nullptr)
|
||||||
|
{
|
||||||
|
(*resource)->Release();
|
||||||
|
*resource = nullptr;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
data_.materials.clear();
|
try_release(&material->textureTable->u.image->textures.map);
|
||||||
});
|
try_release(&material->textureTable->u.image->textures.shaderView);
|
||||||
|
try_release(&material->textureTable->u.image->textures.shaderViewAlternate);
|
||||||
|
|
||||||
|
utils::memory::free(material->textureTable->u.image);
|
||||||
|
utils::memory::free(material->textureTable);
|
||||||
|
utils::memory::free(material->name);
|
||||||
|
utils::memory::free(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -282,7 +204,6 @@ namespace materials
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub);
|
|
||||||
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub);
|
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub);
|
||||||
db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub);
|
db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub);
|
||||||
|
|
||||||
@ -296,7 +217,7 @@ namespace materials
|
|||||||
|
|
||||||
scheduler::once([]
|
scheduler::once([]
|
||||||
{
|
{
|
||||||
debug_materials = dvars::register_bool("debug_materials", 0, 0x0, "Print current material and images");
|
debug_materials = dvars::register_bool("debug_materials", false, game::DVAR_FLAG_NONE, "Print current material and images");
|
||||||
}, scheduler::main);
|
}, scheduler::main);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
namespace materials
|
namespace materials
|
||||||
{
|
{
|
||||||
void add(const std::string& name, const std::string& data);
|
bool setup_material_image(game::Material* material, const std::string& data);
|
||||||
bool exists(const std::string& name);
|
game::Material* create_material(const std::string& name);
|
||||||
void clear();
|
void free_material(game::Material* material);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ namespace mods
|
|||||||
{
|
{
|
||||||
if (release_assets)
|
if (release_assets)
|
||||||
{
|
{
|
||||||
materials::clear();
|
|
||||||
fonts::clear();
|
fonts::clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ namespace party
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
connection_state server_connection_state{};
|
connection_state server_connection_state{};
|
||||||
discord_information server_discord_information{};
|
std::optional<discord_information> server_discord_info{};
|
||||||
|
|
||||||
struct usermap_file
|
struct usermap_file
|
||||||
{
|
{
|
||||||
@ -755,9 +755,9 @@ namespace party
|
|||||||
return server_connection_state;
|
return server_connection_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
discord_information get_server_discord_information()
|
std::optional<discord_information> get_server_discord_info()
|
||||||
{
|
{
|
||||||
return server_discord_information;
|
return server_discord_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -1152,8 +1152,14 @@ namespace party
|
|||||||
server_connection_state.motd = info.get("sv_motd");
|
server_connection_state.motd = info.get("sv_motd");
|
||||||
server_connection_state.max_clients = std::stoi(info.get("sv_maxclients"));
|
server_connection_state.max_clients = std::stoi(info.get("sv_maxclients"));
|
||||||
server_connection_state.base_url = info.get("sv_wwwBaseUrl");
|
server_connection_state.base_url = info.get("sv_wwwBaseUrl");
|
||||||
server_discord_information.image = info.get("sv_discordImageUrl");
|
|
||||||
server_discord_information.image_text = info.get("sv_discordImageText");
|
discord_information discord_info{};
|
||||||
|
discord_info.image = info.get("sv_discordImageUrl");
|
||||||
|
discord_info.image_text = info.get("sv_discordImageText");
|
||||||
|
if (!discord_info.image.empty() || !discord_info.image_text.empty())
|
||||||
|
{
|
||||||
|
server_discord_info.emplace(discord_info);
|
||||||
|
}
|
||||||
|
|
||||||
connect_to_party(target, mapname, gametype);
|
connect_to_party(target, mapname, gametype);
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ namespace party
|
|||||||
|
|
||||||
void clear_sv_motd();
|
void clear_sv_motd();
|
||||||
connection_state get_server_connection_state();
|
connection_state get_server_connection_state();
|
||||||
discord_information get_server_discord_information();
|
std::optional<discord_information> get_server_discord_info();
|
||||||
|
|
||||||
int get_client_num_by_name(const std::string& name);
|
int get_client_num_by_name(const std::string& name);
|
||||||
|
|
||||||
|
@ -376,7 +376,19 @@ namespace ui_scripting
|
|||||||
lua["discord"] = discord_table;
|
lua["discord"] = discord_table;
|
||||||
|
|
||||||
discord_table["respond"] = discord::respond;
|
discord_table["respond"] = discord::respond;
|
||||||
discord_table["getavatarmaterial"] = discord::get_avatar_material;
|
|
||||||
|
discord_table["getavatarmaterial"] = [](const std::string& id)
|
||||||
|
-> script_value
|
||||||
|
{
|
||||||
|
const auto material = discord::get_avatar_material(id);
|
||||||
|
if (material == nullptr)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return lightuserdata(material);
|
||||||
|
};
|
||||||
|
|
||||||
discord_table["reply"] = table();
|
discord_table["reply"] = table();
|
||||||
discord_table["reply"]["yes"] = DISCORD_REPLY_YES;
|
discord_table["reply"]["yes"] = DISCORD_REPLY_YES;
|
||||||
discord_table["reply"]["ignore"] = DISCORD_REPLY_IGNORE;
|
discord_table["reply"]["ignore"] = DISCORD_REPLY_IGNORE;
|
||||||
|
@ -1542,7 +1542,9 @@ namespace game
|
|||||||
char data[1];
|
char data[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
union $3FA29451CE6F1FA138A5ABAB84BE9676
|
struct GfxTexture
|
||||||
|
{
|
||||||
|
union
|
||||||
{
|
{
|
||||||
ID3D11Texture1D* linemap;
|
ID3D11Texture1D* linemap;
|
||||||
ID3D11Texture2D* map;
|
ID3D11Texture2D* map;
|
||||||
@ -1550,10 +1552,6 @@ namespace game
|
|||||||
ID3D11Texture2D* cubemap;
|
ID3D11Texture2D* cubemap;
|
||||||
GfxImageLoadDef* loadDef;
|
GfxImageLoadDef* loadDef;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GfxTexture
|
|
||||||
{
|
|
||||||
$3FA29451CE6F1FA138A5ABAB84BE9676 ___u0;
|
|
||||||
ID3D11ShaderResourceView* shaderView;
|
ID3D11ShaderResourceView* shaderView;
|
||||||
ID3D11ShaderResourceView* shaderViewAlternate;
|
ID3D11ShaderResourceView* shaderViewAlternate;
|
||||||
};
|
};
|
||||||
|
@ -125,6 +125,8 @@ namespace game
|
|||||||
WEAK symbol<char*(char* string)> I_CleanStr{0x4293E0, 0x5AF2E0};
|
WEAK symbol<char*(char* string)> I_CleanStr{0x4293E0, 0x5AF2E0};
|
||||||
|
|
||||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1AC410, 0x199990};
|
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1AC410, 0x199990};
|
||||||
|
WEAK symbol<int(const char* cmd)> Key_GetBindingForCmd{0x377280, 0x1572B0};
|
||||||
|
WEAK symbol<void(int local_client_num, int keynum, int binding)> Key_SetBinding{0x1AC570, 0x199AE0};
|
||||||
|
|
||||||
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x0, 0x1A5C10};
|
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x0, 0x1A5C10};
|
||||||
|
|
||||||
@ -347,6 +349,8 @@ namespace game
|
|||||||
|
|
||||||
WEAK symbol<map_t> maps{0x7CE5A0, 0x926C80};
|
WEAK symbol<map_t> maps{0x7CE5A0, 0x926C80};
|
||||||
|
|
||||||
|
WEAK symbol<ID3D11Device*> d3d11_device{0x1163B98, 0x12DFBF8};
|
||||||
|
|
||||||
namespace mp
|
namespace mp
|
||||||
{
|
{
|
||||||
WEAK symbol<gentity_s> g_entities{0x0, 0x71F19E0};
|
WEAK symbol<gentity_s> g_entities{0x0, 0x71F19E0};
|
||||||
|
@ -131,6 +131,14 @@ namespace utils::string
|
|||||||
*out = '\0';
|
*out = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string strip(const std::string& string)
|
||||||
|
{
|
||||||
|
std::string new_string;
|
||||||
|
new_string.resize(string.size(), 0);
|
||||||
|
strip(string.data(), new_string.data(), static_cast<int>(new_string.size()));
|
||||||
|
return new_string;
|
||||||
|
}
|
||||||
|
|
||||||
std::string convert(const std::wstring& wstr)
|
std::string convert(const std::wstring& wstr)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
@ -92,6 +92,7 @@ namespace utils::string
|
|||||||
std::string get_clipboard_data();
|
std::string get_clipboard_data();
|
||||||
|
|
||||||
void strip(const char* in, char* out, int max);
|
void strip(const char* in, char* out, int max);
|
||||||
|
std::string strip(const std::string& string);
|
||||||
|
|
||||||
std::string convert(const std::wstring& wstr);
|
std::string convert(const std::wstring& wstr);
|
||||||
std::wstring convert(const std::string& str);
|
std::wstring convert(const std::string& str);
|
||||||
|
Loading…
Reference in New Issue
Block a user