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