Skip to content

Commit

Permalink
[Kernel] Support contexts as properties
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianCassar committed Feb 11, 2025
1 parent da2dfdb commit b117f6d
Show file tree
Hide file tree
Showing 22 changed files with 635 additions and 304 deletions.
21 changes: 14 additions & 7 deletions src/xenia/app/discord/discord_presence.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
******************************************************************************
*/

#include "discord_presence.h"
#include <ctime>
#include "third_party/discord-rpc/include/discord_rpc.h"

#include <third_party/discord-rpc/include/discord_rpc.h>

#include "xenia/app/discord/discord_presence.h"
#include "xenia/base/string.h"

// TODO: This library has been deprecated in favor of Discord's GameSDK.
Expand Down Expand Up @@ -37,23 +39,28 @@ void DiscordPresence::NotPlaying() {
discordPresence.state = "Idle";
discordPresence.details = "Standby";
discordPresence.largeImageKey = "app";
discordPresence.largeImageText = "Xenia Canary - Experimental Testing branch";
discordPresence.largeImageText = "Xenia Canary - Netplay";
discordPresence.startTimestamp = time(0);
discordPresence.instance = 1;
Discord_UpdatePresence(&discordPresence);
}

void DiscordPresence::PlayingTitle(const std::string_view game_title) {
void DiscordPresence::PlayingTitle(const std::string_view game_title,
const std::string_view state) {
if (!start_time) {
start_time = time(0);
}

auto details = std::string(game_title);
DiscordRichPresence discordPresence = {};
discordPresence.state = "In Game";
discordPresence.state = state.data();
discordPresence.details = details.c_str();
// TODO(gibbed): we don't have state icons yet.
// discordPresence.smallImageKey = "app";
// discordPresence.largeImageKey = "state_ingame";
discordPresence.largeImageKey = "app";
discordPresence.largeImageText = "Xenia Canary - Experimental Testing branch";
discordPresence.startTimestamp = time(0);
discordPresence.largeImageText = "Xenia Canary - Netplay";
discordPresence.startTimestamp = start_time;
discordPresence.instance = 1;
Discord_UpdatePresence(&discordPresence);
}
Expand Down
5 changes: 4 additions & 1 deletion src/xenia/app/discord/discord_presence.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ class DiscordPresence {
public:
static void Initialize();
static void NotPlaying();
static void PlayingTitle(const std::string_view game_title);
static void PlayingTitle(const std::string_view game_titleconst,
std::string_view state);
static void Shutdown();

inline static time_t start_time;
};

} // namespace discord
Expand Down
9 changes: 5 additions & 4 deletions src/xenia/app/xenia_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,8 @@ void EmulatorApp::EmulatorThread() {
emulator_->on_launch.AddListener([&](auto title_id, const auto& game_title) {
if (cvars::discord) {
discord::DiscordPresence::PlayingTitle(
game_title.empty() ? "Unknown Title" : std::string(game_title));
game_title.empty() ? "Unknown Title" : std::string(game_title),
"In Game");
}
app_context().CallInUIThread([this]() { emulator_window_->UpdateTitle(); });
emulator_thread_event_->Set();
Expand All @@ -648,11 +649,11 @@ void EmulatorApp::EmulatorThread() {
emulator_->on_presence_change.AddListener(
[&](const auto& game_title, const auto& presence_string) {
if (cvars::discord) {
std::string title =
const std::string title =
game_title.empty() ? "Unknown Title" : std::string(game_title);

discord::DiscordPresence::PlayingTitle(
fmt::format("{}\n{}", title, xe::to_utf8(presence_string)));
discord::DiscordPresence::PlayingTitle(title,
xe::to_utf8(presence_string));
}
});

Expand Down
9 changes: 9 additions & 0 deletions src/xenia/emulator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,15 @@ X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path,
}
}
}

for (uint32_t i = 0; i < XUserMaxUserCount; i++) {
if (kernel_state()->xam_state()->IsUserSignedIn(i)) {
kernel_state()
->xam_state()
->GetUserProfile(i)
->InitializeSystemContexts();
}
}
}

// Initializing the shader storage in a blocking way so the user doesn't
Expand Down
67 changes: 2 additions & 65 deletions src/xenia/kernel/XLiveAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include <random>

#include <third_party/rapidcsv/src/rapidcsv.h>
#include "third_party/rapidcsv/src/rapidcsv.h"

#include "xenia/base/cvar.h"
#include "xenia/base/logging.h"
Expand Down Expand Up @@ -1131,71 +1131,8 @@ void XLiveAPI::XSessionCreate(uint64_t sessionId, XSessionData* data) {
XELOGI("XSessionCreate POST Success");
}

// A context is a type of property therefore replace with properties endpoint
void XLiveAPI::SessionContextSet(uint64_t session_id,
std::map<uint32_t, uint32_t> contexts) {
std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/context",
kernel_state()->title_id(), session_id);

Document doc;
doc.SetObject();

Value contextsJson(kArrayType);

for (const auto& entry : contexts) {
Value contextJson(kObjectType);
contextJson.AddMember("contextId", entry.first, doc.GetAllocator());
contextJson.AddMember("value", entry.second, doc.GetAllocator());
contextsJson.PushBack(contextJson.Move(), doc.GetAllocator());
}

doc.AddMember("contexts", contextsJson, doc.GetAllocator());

rapidjson::StringBuffer buffer;
PrettyWriter<rapidjson::StringBuffer> writer(buffer);
doc.Accept(writer);

std::unique_ptr<HTTPResponseObjectJSON> response =
Post(endpoint, (uint8_t*)buffer.GetString());

if (response->StatusCode() != HTTP_STATUS_CODE::HTTP_CREATED) {
XELOGE("SessionContextSet error message: {}", response->Message());
assert_always();
}
}

const std::map<uint32_t, uint32_t> XLiveAPI::SessionContextGet(
uint64_t session_id) {
std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/context",
kernel_state()->title_id(), session_id);

std::map<uint32_t, uint32_t> result = {};

std::unique_ptr<HTTPResponseObjectJSON> response = Get(endpoint);

if (response->StatusCode() != HTTP_STATUS_CODE::HTTP_OK) {
XELOGE("SessionContextGet error message: {}", response->Message());
assert_always();

return result;
}

Document doc;
doc.Parse(response->RawResponse().response);

const Value& contexts = doc["context"];

for (auto itr = contexts.MemberBegin(); itr != contexts.MemberEnd(); itr++) {
const uint32_t context_id =
xe::string_util::from_string<uint32_t>(itr->name.GetString(), true);
result.insert({context_id, itr->value.GetUint()});
}

return result;
}

void XLiveAPI::SessionPropertiesAdd(uint64_t session_id,
std::vector<Property> properties) {
std::vector<Property>& properties) {
std::string endpoint = fmt::format("title/{:08X}/sessions/{:016x}/properties",
kernel_state()->title_id(), session_id);

Expand Down
2 changes: 1 addition & 1 deletion src/xenia/kernel/XLiveAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class XLiveAPI {
uint64_t session_id);

static void SessionPropertiesAdd(uint64_t session_id,
std::vector<Property> properties);
std::vector<Property>& properties);

static const std::vector<Property> SessionPropertiesGet(uint64_t session_id);

Expand Down
26 changes: 12 additions & 14 deletions src/xenia/kernel/json/properties_object_json.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,30 @@ PropertiesObjectJSON::PropertiesObjectJSON() : properties_({}) {}
PropertiesObjectJSON::~PropertiesObjectJSON() {}

bool PropertiesObjectJSON::Deserialize(const rapidjson::Value& obj) {
bool valid = false;

if (obj.HasMember("properties")) {
auto& propertiesObj = obj["properties"];

if (!propertiesObj.IsArray()) {
return valid;
return false;
}

valid = true;

for (auto& serialized_property : propertiesObj.GetArray()) {
std::string base64 = serialized_property.GetString();
const std::string base64 = serialized_property.GetString();
std::uint32_t base64_size = static_cast<uint32_t>(base64.size());
std::uint32_t base64_decode_size = AV_BASE64_DECODE_SIZE(base64_size);

uint8_t* data_out = new uint8_t[base64_decode_size];
auto out = av_base64_decode(data_out, base64.c_str(), base64_decode_size);
std::vector<uint8_t> data_out(base64_decode_size);

auto out =
av_base64_decode(data_out.data(), base64.c_str(), base64_decode_size);

const Property property_ = Property(data_out, base64_decode_size);
const Property property_ = Property(data_out.data(), base64_decode_size);

properties_.push_back(property_);
}
}

return valid;
return true;
}

bool PropertiesObjectJSON::Serialize(
Expand All @@ -64,12 +62,12 @@ bool PropertiesObjectJSON::Serialize(
const uint32_t entry_size = static_cast<uint32_t>(entry_data.size());
const uint32_t entry_out_size = AV_BASE64_SIZE(entry_size);

char* entry_serialized = new char[entry_out_size];
auto out = av_base64_encode(entry_serialized, entry_out_size,
std::vector<char> entry_serialized(entry_out_size);

auto out = av_base64_encode(entry_serialized.data(), entry_out_size,
entry_data.data(), entry_size);

std::string base64_out = std::string(entry_serialized);
const uint32_t base64_out_size = static_cast<uint32_t>(base64_out.size());
std::string base64_out = std::string(entry_serialized.data());

writer->String(base64_out);
}
Expand Down
Loading

0 comments on commit b117f6d

Please sign in to comment.