iw4x-client/src/Components/Modules/Friends.cpp

762 lines
21 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
namespace Components
{
2017-02-19 05:38:18 -05:00
bool Friends::LoggedOn = false;
2017-01-31 12:16:30 -05:00
bool Friends::TriggerSort = false;
2017-01-31 11:04:54 -05:00
bool Friends::TriggerUpdate = false;
2017-01-31 12:16:30 -05:00
2017-01-30 15:54:34 -05:00
int Friends::InitialState;
unsigned int Friends::CurrentFriend;
std::recursive_mutex Friends::Mutex;
std::vector<Friends::Friend> Friends::FriendsList;
2022-04-12 17:15:50 -04:00
Dvar::Var Friends::UIStreamFriendly;
Dvar::Var Friends::CLAnonymous;
Dvar::Var Friends::CLNotifyFriendState;
void Friends::SortIndividualList(std::vector<Friends::Friend>* list)
{
2017-07-12 07:39:45 -04:00
std::stable_sort(list->begin(), list->end(), [](Friends::Friend const& friend1, Friends::Friend const& friend2)
{
2017-06-08 11:55:27 -04:00
return friend1.cleanName.compare(friend2.cleanName) < 0;
});
}
2017-01-31 12:16:30 -05:00
void Friends::SortList(bool force)
{
if (!force)
2017-01-31 12:16:30 -05:00
{
Friends::TriggerSort = true;
return;
}
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
2017-01-29 12:06:48 -05:00
std::vector<Friends::Friend> connectedList;
std::vector<Friends::Friend> playingList;
std::vector<Friends::Friend> onlineList;
std::vector<Friends::Friend> offlineList;
// Split up the list
for (auto entry : Friends::FriendsList)
{
if (!entry.online) offlineList.push_back(entry);
else if (!Friends::IsOnline(entry.lastTime)) onlineList.push_back(entry);
2017-01-30 15:13:30 -05:00
else if (entry.server.getType() == Game::NA_BAD) playingList.push_back(entry);
else connectedList.push_back(entry);
}
2017-01-29 12:06:48 -05:00
Friends::SortIndividualList(&connectedList);
Friends::SortIndividualList(&playingList);
Friends::SortIndividualList(&onlineList);
Friends::SortIndividualList(&offlineList);
2017-01-31 10:20:49 -05:00
size_t count = Friends::FriendsList.size();
Friends::FriendsList.clear();
2017-01-31 10:20:49 -05:00
Friends::FriendsList.reserve(count);
2017-01-29 12:06:48 -05:00
Utils::Merge(&Friends::FriendsList, connectedList);
Utils::Merge(&Friends::FriendsList, playingList);
Utils::Merge(&Friends::FriendsList, onlineList);
Utils::Merge(&Friends::FriendsList, offlineList);
}
void Friends::UpdateUserInfo(SteamID user)
{
2017-01-30 15:13:30 -05:00
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
auto entry = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [user](Friends::Friend entry)
{
return (entry.userId.bits == user.bits);
2017-01-30 15:13:30 -05:00
});
if (entry == Friends::FriendsList.end() || !Steam::Proxy::SteamFriends) return;
entry->name = Steam::Proxy::SteamFriends->GetFriendPersonaName(user);
entry->online = Steam::Proxy::SteamFriends->GetFriendPersonaState(user) != 0;
entry->cleanName = Utils::String::ToLower(TextRenderer::StripColors(entry->name));
2017-02-16 21:27:38 -05:00
std::string guid = Friends::GetPresence(user, "iw4x_guid");
std::string name = Friends::GetPresence(user, "iw4x_name");
std::string experience = Friends::GetPresence(user, "iw4x_experience");
std::string prestige = Friends::GetPresence(user, "iw4x_prestige");
if (!guid.empty()) entry->guid.bits = strtoull(guid.data(), nullptr, 16);
if (!name.empty()) entry->playerName = name;
if (!experience.empty()) entry->experience = atoi(experience.data());
if (!prestige.empty()) entry->prestige = atoi(prestige.data());
2017-02-16 21:27:38 -05:00
std::string server = Friends::GetPresence(user, "iw4x_server");
2017-01-30 15:13:30 -05:00
Network::Address oldAddress = entry->server;
bool gotOnline = Friends::IsOnline(entry->lastTime);
2017-02-16 21:27:38 -05:00
entry->lastTime = static_cast<unsigned int>(atoi(Friends::GetPresence(user, "iw4x_playing").data()));
gotOnline = !gotOnline && Friends::IsOnline(entry->lastTime);
2017-01-30 15:13:30 -05:00
if (server.empty())
{
entry->server.setType(Game::NA_BAD);
entry->serverName.clear();
}
else if (entry->server != server)
{
entry->server = server;
entry->serverName.clear();
}
// Block localhost
2017-02-08 08:45:02 -05:00
if (entry->server.getType() == Game::NA_LOOPBACK || (entry->server.getType() == Game::NA_IP && entry->server.getIP().full == 0x0100007F)) entry->server.setType(Game::NA_BAD);
else if (entry->server.getType() != Game::NA_BAD && entry->server != oldAddress)
2017-01-30 15:13:30 -05:00
{
2018-10-09 04:53:15 -04:00
Node::Add(entry->server);
2017-01-30 15:13:30 -05:00
Network::SendCommand(entry->server, "getinfo", Utils::Cryptography::Rand::GenerateChallenge());
}
2017-01-30 15:13:30 -05:00
Friends::SortList();
2022-04-12 17:15:50 -04:00
const auto notify = Friends::CLNotifyFriendState.get<bool>();
if (gotOnline && (!notify || (notify && !Game::CL_IsCgameInitialized())) && !Friends::UIStreamFriendly.get<bool>())
{
Game::Material* material = Friends::CreateAvatar(user);
Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]()
{
Materials::Delete(material, true);
});
}
}
2017-01-31 11:04:54 -05:00
void Friends::UpdateState(bool force)
2017-01-29 09:10:54 -05:00
{
2022-04-12 17:15:50 -04:00
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled()) return;
2017-02-01 15:08:59 -05:00
if (force)
2017-01-30 15:13:30 -05:00
{
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamFriends)
2017-01-31 11:04:54 -05:00
{
int state = Steam::Proxy::SteamFriends->GetPersonaState();
Steam::Proxy::ClientFriends.invoke<void>("SetPersonaState", (state == 1 ? 2 : 1));
2017-01-31 11:04:54 -05:00
}
}
else
{
Friends::TriggerUpdate = true;
2017-01-30 15:13:30 -05:00
}
2017-01-29 09:10:54 -05:00
}
2018-12-17 08:29:18 -05:00
void Friends::UpdateServer(Network::Address server, const std::string& hostname, const std::string& mapname)
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
for (auto& entry : Friends::FriendsList)
{
if (entry.server == server)
{
entry.serverName = hostname;
entry.mapname = mapname;
}
}
}
2017-01-31 11:04:54 -05:00
void Friends::UpdateName()
{
Friends::SetPresence("iw4x_name", Steam::SteamFriends()->GetPersonaName());
Friends::UpdateState();
}
std::vector<int> Friends::GetAppIdList()
{
std::vector<int> ids;
const auto addId = [&](int id)
{
if (std::find(ids.begin(), ids.end(), id) == ids.end())
{
ids.push_back(id);
}
};
addId(0);
addId(10190);
addId(480);
addId(Steam::Proxy::AppId);
if (Steam::Proxy::SteamUtils)
{
addId(Steam::Proxy::SteamUtils->GetAppID());
}
if (Steam::Proxy::SteamFriends)
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
2022-05-31 12:38:09 -04:00
const auto modId = *reinterpret_cast<const unsigned int*>("IW4x") | 0x80000000;
// Split up the list
2022-05-31 12:38:09 -04:00
for (const auto& entry : Friends::FriendsList)
{
Steam::FriendGameInfo info;
if (Steam::Proxy::SteamFriends->GetFriendGamePlayed(entry.userId, &info) && info.m_gameID.modID == modId)
{
addId(info.m_gameID.appID);
}
}
}
return ids;
}
void Friends::SetRawPresence(const char* key, const char* value)
{
2017-02-17 05:59:16 -05:00
if (Steam::Proxy::ClientFriends)
2017-01-30 15:13:30 -05:00
{
// Set the presence for all possible apps that IW4x might have to interact with.
// GetFriendRichPresence only reads values for the app that we are running,
// therefore our friends (and we as well) have to set the presence for those apps.
auto appIds = Friends::GetAppIdList();
for (auto id : appIds)
{
Steam::Proxy::ClientFriends.invoke<void>("SetRichPresence", id, key, value);
}
}
}
2018-12-17 08:29:18 -05:00
void Friends::ClearPresence(const std::string& key)
{
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils)
{
Friends::SetRawPresence(key.data(), nullptr);
2017-01-30 15:13:30 -05:00
}
}
2018-12-17 08:29:18 -05:00
void Friends::SetPresence(const std::string& key, const std::string& value)
{
2022-04-12 17:15:50 -04:00
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils && !Friends::CLAnonymous.get<bool>() && !Friends::IsInvisible() && Steam::Enabled())
2017-01-30 15:13:30 -05:00
{
Friends::SetRawPresence(key.data(), value.data());
2017-01-30 15:13:30 -05:00
}
2017-01-29 15:49:48 -05:00
}
2017-02-16 21:27:38 -05:00
void Friends::RequestPresence(SteamID user)
{
if (Steam::Proxy::ClientFriends)
2017-02-16 21:27:38 -05:00
{
Steam::Proxy::ClientFriends.invoke<void>("RequestFriendRichPresence", Friends::GetGame(user), user);
2017-02-16 21:27:38 -05:00
}
}
2018-12-17 08:29:18 -05:00
std::string Friends::GetPresence(SteamID user, const std::string& key)
2017-02-16 21:27:38 -05:00
{
if (!Steam::Proxy::ClientFriends || !Steam::Proxy::SteamUtils) return "";
2017-02-16 21:27:38 -05:00
std::string result = Steam::Proxy::ClientFriends.invoke<const char*>("GetFriendRichPresence", Friends::GetGame(user), user, key.data());
2017-02-16 21:27:38 -05:00
return result;
}
2017-01-29 15:49:48 -05:00
void Friends::SetServer()
{
Friends::SetPresence("iw4x_server", Network::Address(*Game::connectedHost).getString()); // reinterpret_cast<char*>(0x7ED3F8)
Friends::UpdateState();
}
2017-01-29 15:49:48 -05:00
void Friends::ClearServer()
{
Friends::ClearPresence("iw4x_server");
Friends::UpdateState();
}
2017-01-29 15:49:48 -05:00
bool Friends::IsClientInParty(int /*controller*/, int clientNum)
{
if (clientNum < 0 || clientNum >= ARRAYSIZE(Dedicated::PlayerGuids)) return false;
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
SteamID guid = Dedicated::PlayerGuids[clientNum][0];
2017-01-29 15:49:48 -05:00
for (auto entry : Friends::FriendsList)
{
if (entry.guid.bits == guid.bits && Friends::IsOnline(entry.lastTime) && entry.online)
2017-01-29 15:49:48 -05:00
{
return true;
}
}
return false;
}
2017-01-29 09:10:54 -05:00
void Friends::UpdateRank()
{
2017-06-13 09:35:12 -04:00
static std::optional<int> levelVal;
2017-01-29 09:10:54 -05:00
int experience = Game::Live_GetXp(0);
int prestige = Game::Live_GetPrestige(0);
int level = (experience & 0xFFFFFF) | ((prestige & 0xFF) << 24);
if (!levelVal.has_value() || levelVal.value() != level)
2017-01-29 09:10:54 -05:00
{
2017-06-13 09:35:12 -04:00
levelVal.emplace(level);
2017-01-29 09:10:54 -05:00
2017-01-30 15:54:34 -05:00
Friends::SetPresence("iw4x_experience", Utils::String::VA("%d", experience));
Friends::SetPresence("iw4x_prestige", Utils::String::VA("%d", prestige));
Friends::UpdateState();
2017-01-29 09:10:54 -05:00
}
}
void Friends::UpdateFriends()
{
2017-01-30 15:13:30 -05:00
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
2017-02-19 05:38:18 -05:00
Friends::LoggedOn = (Steam::Proxy::SteamUser_ && Steam::Proxy::SteamUser_->LoggedOn());
2017-01-30 15:13:30 -05:00
if (!Steam::Proxy::SteamFriends) return;
Game::UI_UpdateArenas();
2017-01-30 15:13:30 -05:00
int count = Steam::Proxy::SteamFriends->GetFriendCount(4);
Proto::Friends::List list;
list.ParseFromString(Utils::IO::ReadFile("players/friends.dat"));
std::vector<Friends::Friend> steamFriends;
2017-01-30 15:13:30 -05:00
for (int i = 0; i < count; ++i)
{
SteamID id = Steam::Proxy::SteamFriends->GetFriendByIndex(i, 4);
Friends::Friend entry;
entry.userId = id;
entry.guid.bits = 0;
2017-01-30 15:13:30 -05:00
entry.online = false;
entry.lastTime = 0;
2017-01-30 15:13:30 -05:00
entry.prestige = 0;
entry.experience = 0;
entry.server.setType(Game::NA_BAD);
for (auto storedFriend : list.friends())
{
if (entry.userId.bits == strtoull(storedFriend.steamid().data(), nullptr, 16))
{
entry.playerName = storedFriend.name();
entry.experience = storedFriend.experience();
entry.prestige = storedFriend.prestige();
entry.guid.bits = strtoull(storedFriend.guid().data(), nullptr, 16);
break;
}
}
auto oldEntry = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [id](Friends::Friend entry)
2017-01-30 15:13:30 -05:00
{
return (entry.userId.bits == id.bits);
2017-01-30 15:13:30 -05:00
});
if (oldEntry != Friends::FriendsList.end()) entry = *oldEntry;
else Friends::FriendsList.push_back(entry);
steamFriends.push_back(entry);
}
for (auto i = Friends::FriendsList.begin(); i != Friends::FriendsList.end();)
{
SteamID id = i->userId;
2017-01-30 15:13:30 -05:00
auto oldEntry = std::find_if(steamFriends.begin(), steamFriends.end(), [id](Friends::Friend entry)
{
return (entry.userId.bits == id.bits);
});
2017-01-30 15:13:30 -05:00
if (oldEntry == steamFriends.end())
{
i = Friends::FriendsList.erase(i);
}
else
{
*i = *oldEntry;
++i;
2017-01-30 15:13:30 -05:00
Friends::UpdateUserInfo(id);
2017-02-16 21:27:38 -05:00
Friends::RequestPresence(id);
}
2017-01-30 15:13:30 -05:00
}
}
unsigned int Friends::GetFriendCount()
{
return Friends::FriendsList.size();
}
const char* Friends::GetFriendText(unsigned int index, int column)
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
if (index >= Friends::FriendsList.size()) return "";
auto user = Friends::FriendsList[index];
switch (column)
{
case 0:
{
static char buffer[0x100];
2017-01-26 06:42:42 -05:00
ZeroMemory(buffer, sizeof(buffer));
2017-01-26 06:42:42 -05:00
Game::Material* rankIcon = nullptr;
int rank = Game::CL_GetRankForXP(user.experience);
Game::CL_GetRankIcon(rank, user.prestige, &rankIcon);
2017-01-29 09:10:54 -05:00
if (!rankIcon) rankIcon = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material;
2017-01-26 06:42:42 -05:00
buffer[0] = '^';
buffer[1] = 2;
// Icon size
2017-01-26 06:42:42 -05:00
char size = 0x30;
buffer[2] = size; // Width
buffer[3] = size; // Height
// Icon name length
2018-05-09 08:33:52 -04:00
buffer[4] = static_cast<char>(strlen(rankIcon->info.name));
2018-05-09 08:33:52 -04:00
strcat_s(buffer, rankIcon->info.name);
2017-01-29 09:10:54 -05:00
strcat_s(buffer, Utils::String::VA(" %i", (rank + 1)));
return buffer;
}
case 1:
2017-01-31 11:04:54 -05:00
{
if (user.playerName.empty())
{
return Utils::String::VA("%s", user.name.data());
}
else if (user.name == user.playerName)
{
return Utils::String::VA("%s", user.name.data());
}
2017-01-31 11:04:54 -05:00
else
{
return Utils::String::VA("%s ^7(%s^7)", user.name.data(), user.playerName.data());
}
}
case 2:
{
2017-01-30 15:13:30 -05:00
if (!user.online) return "Offline";
if (!Friends::IsOnline(user.lastTime)) return "Online";
2017-01-30 15:13:30 -05:00
if (user.server.getType() == Game::NA_BAD) return "Playing IW4x";
if (user.serverName.empty()) return Utils::String::VA("Playing on %s", user.server.getCString());
return Utils::String::VA("Playing %s on %s", Game::UI_LocalizeMapName(user.mapname.data()), user.serverName.data());
}
default:
break;
}
return "";
}
void Friends::SelectFriend(unsigned int index)
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
if (index >= Friends::FriendsList.size()) return;
Friends::CurrentFriend = index;
}
__declspec(naked) void Friends::DisconnectStub()
{
__asm
{
pushad
2021-10-03 16:23:26 -04:00
call Friends::ClearServer
2021-10-03 16:23:26 -04:00
call Dvar::ResetDvarsValue
popad
push 467CC0h
retn
}
}
2017-02-16 13:44:21 -05:00
void Friends::AddFriend(SteamID user)
{
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamFriends)
2017-02-16 13:44:21 -05:00
{
if (Steam::Proxy::ClientFriends.invoke<bool>("AddFriend", user))
2017-02-16 13:44:21 -05:00
{
2017-02-18 06:50:02 -05:00
Toast::Show("cardicon_joystick", Steam::Proxy::SteamFriends->GetFriendPersonaName(user), "friend request sent", 3000);
2017-02-16 13:44:21 -05:00
}
else
{
2017-02-16 21:27:38 -05:00
Toast::Show("cardicon_stop", Steam::Proxy::SteamFriends->GetFriendPersonaName(user), "unable to send friend request", 3000);
2017-02-16 13:44:21 -05:00
}
}
}
int Friends::GetGame(SteamID user)
{
int appId = 0;
Steam::FriendGameInfo info;
if (Steam::Proxy::SteamFriends && Steam::Proxy::SteamFriends->GetFriendGamePlayed(user, &info))
{
appId = info.m_gameID.appID;
}
return appId;
}
2019-01-10 15:18:18 -05:00
bool Friends::IsInvisible()
{
return Friends::InitialState == 7;
}
void Friends::UpdateTimeStamp()
{
2017-01-31 14:58:12 -05:00
Friends::SetPresence("iw4x_playing", Utils::String::VA("%d", Steam::SteamUtils()->GetServerRealTime()));
Friends::SetPresence("iw4x_guid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().bits));
}
bool Friends::IsOnline(unsigned __int64 timeStamp)
{
if (!Steam::Proxy::SteamUtils) return false;
static const unsigned __int64 duration = std::chrono::duration_cast<std::chrono::seconds>(5min).count();
2017-01-31 14:58:12 -05:00
return ((Steam::SteamUtils()->GetServerRealTime() - timeStamp) < duration);
}
void Friends::StoreFriendsList()
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
2017-02-19 04:44:45 -05:00
// Only store our cache if we are logged in, otherwise it might be invalid
2017-02-19 05:38:18 -05:00
if (!Friends::LoggedOn) return;
2017-02-19 04:44:45 -05:00
Proto::Friends::List list;
for (auto entry : Friends::FriendsList)
{
Proto::Friends::Friend* friendEntry = list.add_friends();
friendEntry->set_steamid(Utils::String::VA("%llX", entry.userId.bits));
friendEntry->set_guid(Utils::String::VA("%llX", entry.guid.bits));
friendEntry->set_name(entry.playerName);
friendEntry->set_experience(entry.experience);
friendEntry->set_prestige(entry.prestige);
}
Utils::IO::WriteFile("players/friends.dat", list.SerializeAsString());
}
Game::Material* Friends::CreateAvatar(SteamID user)
{
if (!Steam::Proxy::SteamUtils || !Steam::Proxy::SteamFriends) return nullptr;
int index = Steam::Proxy::SteamFriends->GetMediumFriendAvatar(user);
unsigned int width, height;
Steam::Proxy::SteamUtils->GetImageSize(index, &width, &height);
Game::GfxImage* image = Materials::CreateImage(Utils::String::VA("texture_%llX", user.bits), width, height, 1, 0x1000003, D3DFMT_A8R8G8B8);
D3DLOCKED_RECT lockedRect;
2018-05-09 08:33:52 -04:00
image->texture.map->LockRect(0, &lockedRect, nullptr, 0);
unsigned char* buffer = static_cast<unsigned char*>(lockedRect.pBits);
Steam::Proxy::SteamUtils->GetImageRGBA(index, buffer, width * height * 4);
// Swap red and blue channel
for (unsigned int i = 0; i < width * height * 4; i += 4)
{
std::swap(buffer[i + 0], buffer[i + 2]);
}
2017-06-05 07:47:00 -04:00
// Steam rounds the corners and somehow fuck up the pixels there
buffer[3] = 0; // top-left
buffer[(width - 1) * 4 + 3] = 0; // top-right
buffer[((height - 1) * width * 4) + 3] = 0; // bottom-left
buffer[((height - 1) * width * 4) + (width - 1) * 4 + 3] = 0; // bottom-right
2018-05-09 08:33:52 -04:00
image->texture.map->UnlockRect(0);
return Materials::Create(Utils::String::VA("avatar_%llX", user.bits), image);
}
Friends::Friends()
{
2017-02-19 05:38:18 -05:00
Friends::LoggedOn = false;
2022-04-12 17:15:50 -04:00
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled())
return;
2022-04-12 17:15:50 -04:00
Dvar::OnInit([]
{
Friends::UIStreamFriendly = Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_ARCHIVE, "Stream friendly UI");
Friends::CLAnonymous = Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam");
Friends::CLNotifyFriendState = Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status");
});
2017-01-31 14:58:12 -05:00
2017-02-16 13:44:21 -05:00
Command::Add("addFriend", [](Command::Params* params)
{
2022-03-17 14:50:20 -04:00
if (params->size() < 2u)
{
Logger::Print("Usage: %s <Steam ID in hexadecimal format>\n", params->get(0));
return;
}
2017-02-16 13:44:21 -05:00
SteamID id;
id.bits = std::strtoull(params->get(1), nullptr, 16);
2017-02-16 13:44:21 -05:00
Friends::AddFriend(id);
});
// Hook Live_ShowFriendsList
Utils::Hook(0x4D6C70, []()
{
Command::Execute("openmenu popup_friends", true);
}, HOOK_JUMP).install()->quick();
// Callback to update user information
2017-01-30 15:13:30 -05:00
Steam::Proxy::RegisterCallback(336, [](void* data)
{
2017-01-30 15:13:30 -05:00
Friends::FriendRichPresenceUpdate* update = static_cast<Friends::FriendRichPresenceUpdate*>(data);
Friends::UpdateUserInfo(update->m_steamIDFriend);
});
// Persona state has changed
2017-01-30 15:13:30 -05:00
Steam::Proxy::RegisterCallback(304, [](void* data)
{
2017-01-30 15:13:30 -05:00
Friends::PersonaStateChange* state = static_cast<Friends::PersonaStateChange*>(data);
2017-02-16 21:27:38 -05:00
Friends::RequestPresence(state->m_ulSteamID);
});
2017-01-30 15:13:30 -05:00
// Update state when connecting/disconnecting
Utils::Hook(0x403582, Friends::DisconnectStub, HOOK_CALL).install()->quick();
Utils::Hook(0x4CD023, Friends::SetServer, HOOK_JUMP).install()->quick();
// Show blue icons on the minimap
Utils::Hook(0x493130, Friends::IsClientInParty, HOOK_JUMP).install()->quick();
UIScript::Add("LoadFriends", [](UIScript::Token)
{
Friends::UpdateFriends();
});
UIScript::Add("JoinFriend", [](UIScript::Token)
{
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
if (Friends::CurrentFriend >= Friends::FriendsList.size()) return;
auto& user = Friends::FriendsList[Friends::CurrentFriend];
if (user.online && user.server.getType() != Game::NA_BAD)
{
Party::Connect(user.server);
}
else
{
Command::Execute("snd_playLocal exit_prestige", false);
}
});
Scheduler::OnFrame([]()
2017-01-29 09:10:54 -05:00
{
static Utils::Time::Interval timeInterval;
2017-01-31 12:16:30 -05:00
static Utils::Time::Interval sortInterval;
static Utils::Time::Interval stateInterval;
2017-01-31 11:04:54 -05:00
if (*reinterpret_cast<bool*>(0x1AD5690)) // LiveStorage_DoWeHaveStats
2017-01-31 11:04:54 -05:00
{
Friends::UpdateRank();
}
if (timeInterval.elapsed(2min))
{
timeInterval.update();
2017-01-31 12:16:30 -05:00
Friends::UpdateTimeStamp();
Friends::UpdateState();
}
if (stateInterval.elapsed(5s))
2017-01-31 12:16:30 -05:00
{
stateInterval.update();
2017-01-31 11:04:54 -05:00
if (Friends::TriggerUpdate)
2017-01-31 11:04:54 -05:00
{
Friends::TriggerUpdate = false;
Friends::UpdateState(true);
}
}
if (sortInterval.elapsed(1s))
2017-01-31 12:16:30 -05:00
{
sortInterval.update();
2017-01-31 12:16:30 -05:00
if (Friends::TriggerSort)
{
Friends::TriggerSort = false;
Friends::SortList(true);
}
}
2017-01-29 09:10:54 -05:00
});
2017-02-06 15:23:15 -05:00
UIFeeder::Add(61.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend);
2017-05-31 10:09:41 -04:00
Scheduler::OnShutdown([]()
2017-01-30 15:54:34 -05:00
{
Friends::ClearPresence("iw4x_server");
Friends::ClearPresence("iw4x_playing");
2017-01-31 09:56:24 -05:00
#ifdef DEBUG
if (Steam::Proxy::SteamFriends)
{
Steam::Proxy::SteamFriends->ClearRichPresence();
}
2017-01-31 09:56:24 -05:00
#endif
2017-01-30 15:54:34 -05:00
if (Steam::Proxy::ClientFriends)
2017-01-30 15:54:34 -05:00
{
Steam::Proxy::ClientFriends.invoke<void>("SetPersonaState", Friends::InitialState);
2017-01-30 15:54:34 -05:00
}
});
Scheduler::Once([]()
2017-01-30 15:54:34 -05:00
{
if (Steam::Proxy::SteamFriends)
2017-01-30 15:54:34 -05:00
{
2019-01-10 15:18:18 -05:00
Friends::InitialState = Steam::Proxy::SteamFriends->GetFriendPersonaState(Steam::Proxy::SteamUser_->GetSteamID());
2017-01-30 15:54:34 -05:00
}
2022-04-12 17:15:50 -04:00
if (Friends::CLAnonymous.get<bool>() || Friends::IsInvisible() || !Steam::Enabled())
2017-02-01 15:08:59 -05:00
{
2017-02-17 05:59:16 -05:00
if (Steam::Proxy::ClientFriends)
2017-02-01 15:08:59 -05:00
{
2022-04-12 17:15:50 -04:00
for (const auto id : Friends::GetAppIdList())
{
Steam::Proxy::ClientFriends.invoke<void>("ClearRichPresence", id);
}
}
if (Steam::Proxy::SteamFriends)
{
Steam::Proxy::SteamFriends->ClearRichPresence();
2017-02-01 15:08:59 -05:00
}
}
Friends::UpdateTimeStamp();
2017-01-31 11:04:54 -05:00
Friends::UpdateName();
Friends::UpdateState();
2017-01-31 10:20:49 -05:00
2017-01-30 15:54:34 -05:00
Friends::UpdateFriends();
});
}
Friends::~Friends()
{
2017-02-25 09:58:43 -05:00
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return;
2017-01-31 14:58:12 -05:00
Friends::StoreFriendsList();
2017-01-30 16:13:57 -05:00
Steam::Proxy::UnregisterCallback(336);
Steam::Proxy::UnregisterCallback(304);
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
Friends::FriendsList.clear();
}
}