Skip to content

Commit

Permalink
[XAM/UI] Implemented XamShowFriendsUI & XInviteGetAcceptedInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianCassar committed Feb 17, 2025
1 parent 7404379 commit d423171
Show file tree
Hide file tree
Showing 14 changed files with 761 additions and 190 deletions.
140 changes: 69 additions & 71 deletions src/xenia/app/profile_dialogs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,19 @@ extern bool xeDrawProfileContent(ui::ImGuiDrawer* imgui_drawer,
const uint64_t xuid, const uint8_t user_index,
const X_XAMACCOUNTINFO* account,
uint64_t* selected_xuid);
}

extern bool xeDrawFriendContent(ui::ImGuiDrawer* imgui_drawer,
UserProfile* profile,
FriendPresenceObjectJSON& presence,
uint64_t* selected_xuid_,
uint64_t* removed_xuid_);

extern bool xeDrawFriendsContent(
ui::ImGuiDrawer* imgui_drawer, UserProfile* profile,
FriendsContentArgs& args,
std::vector<xe::kernel::FriendPresenceObjectJSON>* presences);

} // namespace xam
} // namespace kernel
namespace app {

Expand Down Expand Up @@ -225,6 +237,7 @@ void ProfileConfigDialog::OnDraw(ImGuiIO& io) {
return;
}
}

void FriendsManagerDialog::OnDraw(ImGuiIO& io) {
if (!has_opened_) {
ImGui::OpenPopup("Friends Manager");
Expand All @@ -239,10 +252,16 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {

const bool is_profile_signed_in = profile == nullptr;

ImVec2 btn_size = ImVec2(ImGui::GetWindowSize().x * 0.4f, 0);
ImVec2 btn2_size = ImVec2(ImGui::GetWindowSize().x * 0.2f, 0);
ImVec2 btn3_size = ImVec2(ImGui::GetWindowSize().x * 0.215f, 0);
float btn_width = (ImGui::GetContentRegionAvail().x * 0.4f) -
(ImGui::GetStyle().ItemSpacing.x * 0.5f);
ImVec2 btn_size = ImVec2(btn_width, 0);

ImGuiViewport* viewport = ImGui::GetMainViewport();

ImVec2 center = ImVec2(viewport->Pos.x + viewport->Size.x * 0.5f,
viewport->Pos.y + viewport->Size.y * 0.5f);

ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Friends Manager", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
if (is_profile_signed_in) {
Expand All @@ -251,21 +270,49 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {
}

ImGui::BeginDisabled(is_profile_signed_in);
if (ImGui::Button("Add Friend", btn_size)) {
ImGui::OpenPopup("Add Friend");
if (ImGui::Button("Friends", btn_size)) {
args.friends_open = true;
ImGui::OpenPopup("Friends");
}
ImGui::EndDisabled();

ImGui::SameLine();

ImGui::BeginDisabled(is_profile_signed_in);
if (ImGui::Button("Remove All Friends", btn_size)) {
ImGui::OpenPopup("Remove All Friends");
if (ImGui::Button("Add Friend", btn_size)) {
add_friend_open = true;
ImGui::OpenPopup("Add Friend");
}
ImGui::EndDisabled();

if (ImGui::BeginPopupModal("Add Friend", nullptr,
if (ImGui::Button("Refresh Presence", btn_size)) {
emulator_window_->emulator()->kernel_state()->BroadcastNotification(
kXNotificationFriendsPresenceChanged, user_index);

emulator_window_->emulator()
->display_window()
->app_context()
.CallInUIThread([&]() {
new xe::ui::HostNotificationWindow(
imgui_drawer(), "Refreshed Presence", "Success", 0);
});
}

ImGui::SameLine();

if (ImGui::Button("Exit", btn_size)) {
ImGui::CloseCurrentPopup();
emulator_window_->ToggleFriendsDialog();
}

ImGui::SetNextWindowContentSize(ImVec2(180, 0));
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Add Friend", &add_friend_open,
ImGuiWindowFlags_AlwaysAutoResize)) {
float btn_width = (ImGui::GetContentRegionAvail().x * 0.5f) -
(ImGui::GetStyle().ItemSpacing.x * 0.5f);
ImVec2 half_width_btn = ImVec2(btn_width, 0);

const uint32_t user_index = 0;

if (are_friends) {
Expand All @@ -278,7 +325,7 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {
uint64_t xuid = 0;

if (xuid_string.length() == 16) {
if (xuid_string.substr(0, 4) == "0009") {
if (xuid_string.starts_with("0009")) {
xuid = string_util::from_string<uint64_t>(xuid_string, true);

valid_xuid = IsOnlineXUID(xuid);
Expand All @@ -298,19 +345,22 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {

ImGui::SameLine();

const float window_width = ImGui::GetContentRegionAvail().x;

const std::string friends_count =
fmt::format("\t\t\t\t\t\t\t\t{}/100", profile->GetFriendsCount());
fmt::format("{}/100", profile->GetFriendsCount());

ImGui::Text(friends_count.c_str());
ImGui::SetCursorPosX((ImGui::GetCursorPosX() + window_width -
ImGui::CalcTextSize(friends_count.c_str()).x));

ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
ImGui::Text(friends_count.c_str());

ImGui::InputTextWithHint("##AddFriend", "0009XXXXXXXXXXXX", add_xuid_,
sizeof(add_xuid_),
ImGuiInputTextFlags_CharsHexadecimal |
ImGuiInputTextFlags_CharsUppercase);

if (ImGui::Button("Paste Clipboard", btn3_size)) {
if (ImGui::Button("Paste Clipboard", half_width_btn)) {
const char* clipboard = ImGui::GetClipboardText();

if (clipboard) {
Expand All @@ -325,7 +375,7 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {
ImGui::SameLine();

ImGui::BeginDisabled(!valid_xuid || are_friends);
if (ImGui::Button("Add", btn3_size)) {
if (ImGui::Button("Add", half_width_btn)) {
profile->AddFriendFromXUID(xuid);
xe::kernel::XLiveAPI::AddFriend(xuid);

Expand All @@ -342,68 +392,16 @@ void FriendsManagerDialog::OnDraw(ImGuiIO& io) {
}
ImGui::EndDisabled();

ImGui::SameLine();

if (ImGui::Button("Close", btn3_size)) {
ImGui::CloseCurrentPopup();
}

ImGui::EndPopup();
}

if (ImGui::BeginPopupModal("Remove All Friends", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Are you sure?");
ImGui::Separator();

if (ImGui::Button("Yes", btn2_size)) {
for (const auto& friend_ : profile->GetFriends()) {
profile->RemoveFriend(friend_.xuid);
xe::kernel::XLiveAPI::RemoveFriend(friend_.xuid);
}

emulator_window_->emulator()->kernel_state()->BroadcastNotification(
kXNotificationFriendsFriendRemoved, user_index);

emulator_window_->emulator()
->display_window()
->app_context()
.CallInUIThread([&]() {
new xe::ui::HostNotificationWindow(
imgui_drawer(), "Removed All Friends", "Success", 0);
});

ImGui::CloseCurrentPopup();
}

ImGui::SameLine();

if (ImGui::Button("Cancel", btn2_size)) {
ImGui::CloseCurrentPopup();
}

ImGui::EndPopup();
if (!args.friends_open) {
args.refersh_presence_sync = true;
presences = {};
}

if (ImGui::Button("Refresh Presence", btn_size)) {
emulator_window_->emulator()->kernel_state()->BroadcastNotification(
kXNotificationFriendsPresenceChanged, user_index);
xeDrawFriendsContent(imgui_drawer(), profile, args, &presences);

emulator_window_->emulator()
->display_window()
->app_context()
.CallInUIThread([&]() {
new xe::ui::HostNotificationWindow(
imgui_drawer(), "Refreshed Presence", "Success", 0);
});
}

ImGui::SameLine();

if (ImGui::Button("Exit", btn_size)) {
ImGui::CloseCurrentPopup();
emulator_window_->ToggleFriendsDialog();
}
ImGui::EndPopup();
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/xenia/app/profile_dialogs.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@
#ifndef XENIA_APP_PROFILE_DIALOGS_H_
#define XENIA_APP_PROFILE_DIALOGS_H_

#include "xenia/kernel/json/friend_presence_object_json.h"
#include "xenia/ui/imgui_dialog.h"
#include "xenia/ui/imgui_drawer.h"
#include "xenia/xbox.h"

namespace xe {
namespace kernel {
namespace xam {
struct FriendsContentArgs {
bool friends_open;
bool filter_joinable;
bool filter_title;
bool filter_offline;
bool refersh_presence;
bool refersh_presence_sync;
};
} // namespace xam
} // namespace kernel
namespace app {

class EmulatorWindow;
Expand Down Expand Up @@ -80,6 +93,12 @@ class FriendsManagerDialog final : public ui::ImGuiDialog {
bool are_friends = false;
bool valid_xuid = false;
char add_xuid_[17] = "";
uint64_t selected_xuid_ = 0;
uint64_t removed_xuid_ = 0;
bool add_friend_open = false;
xe::kernel::xam::FriendsContentArgs args = {};
std::vector<xe::kernel::FriendPresenceObjectJSON> presences;
ImGuiTextFilter filter;
EmulatorWindow* emulator_window_;
};

Expand Down
72 changes: 72 additions & 0 deletions src/xenia/kernel/XLiveAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,78 @@ std::unique_ptr<HTTPResponseObjectJSON> XLiveAPI::PraseResponse(
return response;
}

std::vector<xe::kernel::FriendPresenceObjectJSON>
XLiveAPI::GetAllFriendsPresence(const uint32_t user_index) {
const auto profile = kernel_state()->xam_state()->GetUserProfile(user_index);

auto offline_peer_presences = GetOfflineFriendsPresence(user_index);
std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON>
online_peer_presences = {};

if (kernel::XLiveAPI::IsConnectedToServer()) {
online_peer_presences = GetOnlineFriendsPresence(user_index);
}

auto& merged_peer_presences = online_peer_presences;

merged_peer_presences.merge(offline_peer_presences);

std::vector<xe::kernel::FriendPresenceObjectJSON> peer_presences;

std::ranges::transform(
merged_peer_presences, std::back_inserter(peer_presences),
&std::pair<const uint64_t, xe::kernel::FriendPresenceObjectJSON>::second);

std::sort(peer_presences.begin(), peer_presences.end(),
[](const xe::kernel::FriendPresenceObjectJSON& peer_1,
xe::kernel::FriendPresenceObjectJSON& peer_2) {
bool peer_1_prefix = peer_1.Gamertag().starts_with("0009");
bool peer_2_prefix = peer_2.Gamertag().starts_with("0009");

if (peer_1_prefix != peer_2_prefix) {
return !peer_1_prefix;
}

return peer_1.Gamertag() < peer_2.Gamertag();
});

return peer_presences;
}

std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON>
XLiveAPI::GetOfflineFriendsPresence(const uint32_t user_index) {
const auto profile = kernel_state()->xam_state()->GetUserProfile(user_index);

std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON> peer_presences = {};

for (const auto& xuid : profile->GetFriendsXUIDs()) {
xe::kernel::FriendPresenceObjectJSON peer = {};
peer.Gamertag(std::format("{:016X}", xuid));
peer.XUID(xuid);

peer_presences[xuid] = peer;
}

return peer_presences;
}

std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON>
XLiveAPI::GetOnlineFriendsPresence(const uint32_t user_index) {
const auto profile = kernel_state()->xam_state()->GetUserProfile(user_index);

std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON> peer_presences = {};

const auto freinds_presence =
xe::kernel::XLiveAPI::GetFriendsPresence(profile->GetFriendsXUIDs())
->PlayersPresence();

for (const auto& presence : freinds_presence) {
peer_presences[presence.XUID()] = presence;
}

return peer_presences;
}

const uint8_t* XLiveAPI::GenerateMacAddress() {
uint8_t* mac_address = new uint8_t[6];
// MAC OUI part for MS devices.
Expand Down
9 changes: 9 additions & 0 deletions src/xenia/kernel/XLiveAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ class XLiveAPI {
static std::unique_ptr<HTTPResponseObjectJSON> PraseResponse(
response_data response);

static std::vector<xe::kernel::FriendPresenceObjectJSON>
GetAllFriendsPresence(const uint32_t user_index);

static std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON>
GetOfflineFriendsPresence(const uint32_t user_index);

static std::map<uint64_t, xe::kernel::FriendPresenceObjectJSON>
GetOnlineFriendsPresence(const uint32_t user_index);

static const uint8_t* GenerateMacAddress();

static const uint8_t* GetMACaddress();
Expand Down
8 changes: 8 additions & 0 deletions src/xenia/kernel/json/friend_presence_object_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <vector>

#include "xenia/base/string_util.h"
#include "xenia/kernel/json/base_object_json.h"
#include "xenia/kernel/xnet.h"

Expand Down Expand Up @@ -43,6 +44,13 @@ class FriendPresenceObjectJSON : public BaseObjectJSON {
const xe::be<uint64_t>& SessionID() const { return sessionId_; }
void SessionID(const xe::be<uint64_t>& sessionId) { sessionId_ = sessionId; }

const uint32_t TitleIDValue() const {
if (TitleID().empty()) {
return 0;
} else {
return xe::string_util::from_string<uint32_t>(TitleID(), true);
}
}
const std::string& TitleID() const { return title_id_; }
void TitleID(const std::string& titleID) { title_id_ = titleID; }

Expand Down
Loading

0 comments on commit d423171

Please sign in to comment.