diff --git a/data/menu/nullifiedcat/misc/autoparty.xml b/data/menu/nullifiedcat/misc/autoparty.xml index 983d1de3..43bffe63 100755 --- a/data/menu/nullifiedcat/misc/autoparty.xml +++ b/data/menu/nullifiedcat/misc/autoparty.xml @@ -9,6 +9,8 @@ + + diff --git a/src/hacks/AutoParty.cpp b/src/hacks/AutoParty.cpp index 40e4bb08..3ae7d2a2 100644 --- a/src/hacks/AutoParty.cpp +++ b/src/hacks/AutoParty.cpp @@ -3,10 +3,56 @@ * * Created on: Nov 27, 2020 * Author: delimeats-ch + * + * ... + * :ssssssssoooo+////:` + * `:osyyyyyyyyyhdhyyyyyho. + * -oosyyyyyyyyyyyyyyyhhyydhs- + * .soosyyssssyyyyyyyyyyyyyddhh+ + * odsssssyhhhhyyyyyyyyhhhhddhhyo` + * syhhhhhyyyyso++osyyyyhyhhhhyhy+` + * :shdhhyyso+++/////+osyyyyyyyyyyyys: + * .ydmddyssssooo++////++oosyyyyyyyyyhhys:` + * `smmmmdysssyhdys++++++oossyyyyyyhhyyyyyys` + * `/shhhyyyhhhyso+//++oossyyyyyhhhhyyyyyyy/` + * :ssssoo++++++oossyyyhhhhhyyyyyyyyyys:` + * `+oo+++++++oossyyyhhhhhyyyyyyyyyyyhhhy+-` + * .+++++++++oossyyyyyyyyyyyysyyyyyyhhhhhhys. + * .++++++++++oosssssssssssssssyyhhhhhhhhhyy+ + * .+++++++//++oooooooooooosssyyhhhhhhdhhhyyy/ + * -++++++////++++++++ooossssyyyyyhhhdhhyyyyhh- + * :++++++++++/+++ooooooossooosyyyhhhdhyyyyhhdy. + * :+++++++++++++++++++++ooooosyyhhhhhhyyyhhddds + * -+++oo++++++++++++++++++ooyyyyhhhhhyyhhhdddy- + * .+ooooo+++++++++++++++oosyyyyhhhhhyyhhhhhds. + * `+oossoo++++oooooooooossyyyhhhhhhhhhhhhys/ + * `osooooooo++oooooooosyyyyyyhhhhhhhhhhhy. + * osooooooooooooooosssyyyyyhhhdhhhhhhhdh` + * `osoo+++osysssssssossyyyyyhhdddhhhhhhds + * .oooo++oshdhyssssysossyyyyhhdmddddhhddo + * `.-:+ooooo++oyhhdhysssssoossyyyhhdddddhhddd: + * `-:++ossyyysoooooshhddyysssssosssyyyhhdmddddhdds` + * `sssysso+/:-ooooosyyyysoooooooossyyhhhhddmddddy/` + * .--:. :soossyhysoooo+++ooossyyhhhdmmmds/. + * ``--:/osoossyyyo+++++ooossosyyyhhhdhy+. + * `/+ossssssooooo:``....`....-/ossyyhhh:` + * :yysoooooo+o+:` -ssyyhh- + * `:ooooooooo:` .sysyhy` + * -///++/:` +sssyys + * `````` + * _ _ + * | | | | + * ___| |__ ___ ___ _ __ ___ ___| |__ _ _ _ __ __ _ ___ _ __ + * / __| '_ \ / _ \/ _ \ '_ ` _ \/ __| '_ \| | | | '__/ _` |/ _ \ '__| + * | (__| | | | __/ __/ | | | | \__ \ |_) | |_| | | | (_| | __/ | + * \___|_| |_|\___|\___|_| |_| |_|___/_.__/ \__,_|_| \__, |\___|_| + * __/ | + * |___/ */ #include "common.hpp" #include "hack.hpp" +#include "ipc.hpp" namespace hacks::tf2::autoparty { @@ -30,6 +76,10 @@ static settings::Boolean autoparty_log{ "autoparty.log", "true" }; static settings::Boolean message_kicks{ "autoparty.message-kicks", "true" }; // Extra debugging information like locking/unlocking the party static settings::Boolean autoparty_debug{ "autoparty.debug", "false" }; +// Use the longest-running IPC members as party hosts +static settings::Boolean ipc_mode{ "autoparty.ipc-mode", "false" }; +// How many IPC members to use as party hosts +static settings::Int ipc_count{ "autoparty.ipc-count", "0" }; // How often to run the autoparty routine, in seconds static settings::Int timeout{ "autoparty.run-frequency", "60" }; // Only run the autoparty routine once every N seconds @@ -46,12 +96,48 @@ static std::vector party_hosts = {}; if (*autoparty_debug) \ logging::Info("AutoParty (debug): " __VA_ARGS__) +// I can't think of a better way to do this right now +struct ipc_peer +{ + uint32 friendid; + time_t ts_injected; +}; + +bool compare_ts(ipc_peer &a, ipc_peer &b) +{ + return a.ts_injected > b.ts_injected; +} + // Re-populates party_hosts from the current or new configuration void repopulate(std::string str) { // Empty previous values party_hosts.clear(); + // IPC autoparty mode + if (*ipc_mode) + { + if (!ipc::peer) + { + // uh oh + return; + } + auto &peer_data = ipc::peer->memory->peer_user_data; + std::vector sorted_peers = {}; + for (int i = 0; i < cat_ipc::max_peers; i++) + { + ipc::user_data_s &data = peer_data[i]; + struct ipc_peer peer = { .friendid = data.friendid, .ts_injected = data.ts_injected }; + sorted_peers.push_back(peer); + } + std::sort(sorted_peers.begin(), sorted_peers.end(), compare_ts); + for (int i = 0; i < *ipc_count; i++) + { + party_hosts.push_back(sorted_peers[i].friendid); + } + return; + } + // Add Steam32 IDs to party_hosts std::stringstream ss(str); for (uint32 id; ss >> id;) @@ -108,6 +194,8 @@ void leave_party(re::CTFPartyClient *client, bool was_leader) { log("Leaving the party because %d/%d members are offline", client->GetNumMembers() - client->GetNumOnlineMembers(), client->GetNumMembers()); hack::ExecuteCommand("tf_party_leave"); + if (was_leader and *auto_unlock) + unlock_party(); } // Automatically join/leave parties and kick bad members @@ -146,8 +234,11 @@ void party_routine() if (is_host()) { // We are a party host but have no members; allow access to the party - log_debug("No members; unlocking the party"); - unlock_party(); + if (*auto_unlock) + { + log_debug("No members; unlocking the party"); + unlock_party(); + } } else { @@ -157,79 +248,90 @@ void party_routine() else { // We are in a party! - // Get a list of party members, then check each one to determine the leader - std::vector members = client->GetPartySteamIDs(); - uint32 leader_id = 0; - for (int i = 0; i < members.size(); i++) + // Are we the *designated* leader of the current party? + if (is_host()) { - CSteamID id = CSteamID(members[i], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); - // Are you my mummy? - if (client->GetCurrentPartyLeader(id)) + // And are we actually the leader? + // Get a list of party members, then check each one to determine the leader + std::vector members = client->GetPartySteamIDs(); + uint32 leader_id = 0; + for (int i = 0; i < members.size(); i++) { - leader_id = members[i]; - break; - } - } - - // Are we the leader of the current party? - // If so, manage it - if (leader_id == g_ISteamUser->GetSteamID().GetAccountID()) - { - // If a member is offline, just leave the party and allow new join requests - if (*auto_leave and client->GetNumMembers() > client->GetNumOnlineMembers()) - { - leave_party(client, true); - return; - } - - // If enabled, check for any raged players who may have joined our party and kick them - // If there are any, return so we don't kick other members in the event we're also over the set size limit - if (*kick_rage) - { - bool should_ret = false; - for (int i = 0; i < members.size(); i++) + CSteamID id = CSteamID(members[i], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); + // Are you my mummy? + if (client->GetCurrentPartyLeader(id)) { - auto &pl = playerlist::AccessData(members[i]); - if (pl.state == playerlist::k_EState::RAGE) + leader_id = members[i]; + break; + } + } + if (leader_id == g_ISteamUser->GetSteamID().GetAccountID()) + { + // Great, let's manage it + // If a member is offline, just leave the party and allow new join requests + if (*auto_leave and client->GetNumMembers() > client->GetNumOnlineMembers()) + { + leave_party(client, true); + return; + } + + // If enabled, check for any raged players who may have joined our party and kick them + // If there are any, return so we don't kick other members in the event we're also over the set size limit + if (*kick_rage) + { + bool should_ret = false; + for (int i = 0; i < members.size(); i++) { - std::string message = "Kicking Steam32 ID " + std::to_string(members[i]) + " from the party because they are set to RAGE"; - logging::Info("AutoParty: %s", message); - if (*message_kicks) - client->SendPartyChat(message.c_str()); - CSteamID id = CSteamID(members[i], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); + auto &pl = playerlist::AccessData(members[i]); + if (pl.state == playerlist::k_EState::RAGE) + { + std::string message = "Kicking Steam32 ID " + std::to_string(members[i]) + " from the party because they are set to RAGE"; + logging::Info("AutoParty: %s", message); + if (*message_kicks) + client->SendPartyChat(message.c_str()); + CSteamID id = CSteamID(members[i], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); + client->KickPlayer(id); + should_ret = true; + } + } + if (should_ret) + return; + } + + // If we are at or over the specified limit, lock the party so we auto-reject future join requests + if (*auto_lock and members.size() >= *max_size) + { + log_debug("Locking the party because we have %d out of %d allowed members", members.size(), *max_size); + lock_party(); + } + + // Kick extra members from the party + if (members.size() > *max_size) + { + int num_to_kick = members.size() - *max_size; + std::string message = "Kicking " + std::to_string(num_to_kick) + " party members because there are " + std::to_string(members.size()) + " out of " + std::to_string(*max_size) + " allowed members"; + logging::Info("AutoParty: %s", message); + if (*message_kicks) + client->SendPartyChat(message.c_str()); + for (int i = 0; i < num_to_kick; i++) + { + CSteamID id = CSteamID(members[members.size() - (1 + i)], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); client->KickPlayer(id); - should_ret = true; } } - if (should_ret) - return; - } - // If we are at or over the specified limit, lock the party so we auto-reject future join requests - if (*auto_lock and members.size() >= *max_size) + // Unlock the party if it's not full + if (*auto_unlock and members.size() < *max_size) + unlock_party(); + } + else { - log_debug("Locking the party because we have %d out of %d allowed members", members.size(), *max_size); - lock_party(); + // We are in someone else's party as a leader! + // Leave the party and unlock our join request mode + hack::ExecuteCommand("tf_party_leave"); + if (*auto_unlock) + unlock_party(); } - - // Kick extra members from the party - if (members.size() > *max_size) - { - int num_to_kick = members.size() - *max_size; - std::string message = "Kicking " + std::to_string(num_to_kick) + " party members because there are " + std::to_string(members.size()) + " out of " + std::to_string(*max_size) + " allowed members"; - logging::Info("AutoParty: %s", message); - if (*message_kicks) - client->SendPartyChat(message.c_str()); - for (int i = 0; i < num_to_kick; i++) - { - CSteamID id = CSteamID(members[members.size() - (1 + i)], EUniverse::k_EUniversePublic, EAccountType::k_EAccountTypeIndividual); - client->KickPlayer(id); - } - } - - // Unlock the party if it's not full - if (*auto_unlock and members.size() < *max_size) - unlock_party(); } else { @@ -252,6 +354,7 @@ void party_routine() static InitRoutine init([]() { host_list.installChangeCallback([](settings::VariableBase &var, std::string after) { repopulate(after); }); + ipc_mode.installChangeCallback([](settings::VariableBase &var, bool after) { party_hosts.clear(); }); EC::Register(EC::Paint, party_routine, "paint_autoparty", EC::average); }); } // namespace hacks::tf2::autoparty