summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbunnei <bunneidev@gmail.com>2017-07-13 03:31:12 +0200
committerGitHub <noreply@github.com>2017-07-13 03:31:12 +0200
commit9cf261ba8ba4fd9929d275cc793d48d13df624f3 (patch)
tree2eb47ab96bde081a84c5dd7c8107a21cb8a3511c
parentMerge pull request #2815 from mailwl/bossp (diff)
parentweb_backend: Specify api-version on JSON post. (diff)
downloadyuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.gz
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.bz2
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.lz
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.xz
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.zst
yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.zip
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt5
-rw-r--r--externals/CMakeLists.txt12
m---------externals/cpr0
m---------externals/json0
-rw-r--r--src/CMakeLists.txt3
-rw-r--r--src/citra/config.cpp4
-rw-r--r--src/citra/default_ini.h4
-rw-r--r--src/citra_qt/configuration/config.cpp12
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/core/CMakeLists.txt3
-rw-r--r--src/core/settings.h3
-rw-r--r--src/core/telemetry_session.cpp10
-rw-r--r--src/web_service/CMakeLists.txt14
-rw-r--r--src/web_service/telemetry_json.cpp87
-rw-r--r--src/web_service/telemetry_json.h54
-rw-r--r--src/web_service/web_backend.cpp52
-rw-r--r--src/web_service/web_backend.h31
19 files changed, 301 insertions, 3 deletions
diff --git a/.gitmodules b/.gitmodules
index ac0df914d..45ff650ef 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -28,3 +28,9 @@
[submodule "externals/enet"]
path = externals/enet
url = https://github.com/lsalzman/enet
+[submodule "cpr"]
+ path = externals/cpr
+ url = https://github.com/whoshuu/cpr.git
+[submodule "json"]
+ path = externals/json
+ url = https://github.com/nlohmann/json.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4668d4bea..ad73cf495 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -11,6 +11,8 @@ option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF)
option(ENABLE_QT "Enable the Qt frontend" ON)
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
+option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
+
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit
@@ -223,6 +225,9 @@ if (ENABLE_QT)
find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT})
endif()
+if (ENABLE_WEB_SERVICE)
+ add_definitions(-DENABLE_WEB_SERVICE)
+endif()
# Platform-specific library requirements
# ======================================
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index cc47166fc..ccc7f13b6 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -52,3 +52,15 @@ endif()
# ENet
add_subdirectory(enet)
target_include_directories(enet INTERFACE ./enet/include)
+
+if (ENABLE_WEB_SERVICE)
+ # CPR
+ option(BUILD_TESTING OFF)
+ option(BUILD_CPR_TESTS OFF)
+ add_subdirectory(cpr)
+ target_include_directories(cpr INTERFACE ./cpr/include)
+
+ # JSON
+ add_library(json-headers INTERFACE)
+ target_include_directories(json-headers INTERFACE ./json/src)
+endif()
diff --git a/externals/cpr b/externals/cpr
new file mode 160000
+Subproject b5758fbc88021437f968fe5174f121b8b92f5d5
diff --git a/externals/json b/externals/json
new file mode 160000
+Subproject d3496347fcd1382896fca3aaf78a0d803c2f52e
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 655bd83aa..e11940f59 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,3 +14,6 @@ endif()
if (ENABLE_QT)
add_subdirectory(citra_qt)
endif()
+if (ENABLE_WEB_SERVICE)
+ add_subdirectory(web_service)
+endif()
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 957d8dc86..69247b166 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -151,6 +151,10 @@ void Config::ReadValues() {
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port =
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
+
+ // Web Service
+ Settings::values.telemetry_endpoint_url = sdl2_config->Get(
+ "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
}
void Config::Reload() {
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index d8a8fe44f..a12498e0f 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -168,5 +168,9 @@ log_filter = *:Info
# Port for listening to GDB connections.
use_gdbstub=false
gdbstub_port=24689
+
+[WebService]
+# Endpoint URL for submitting telemetry data
+telemetry_endpoint_url =
)";
}
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 64ffc9152..40142b6d9 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -133,6 +133,13 @@ void Config::ReadValues() {
Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt();
qt_config->endGroup();
+ qt_config->beginGroup("WebService");
+ Settings::values.telemetry_endpoint_url =
+ qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
+ .toString()
+ .toStdString();
+ qt_config->endGroup();
+
qt_config->beginGroup("UI");
qt_config->beginGroup("UILayout");
@@ -268,6 +275,11 @@ void Config::SaveValues() {
qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port);
qt_config->endGroup();
+ qt_config->beginGroup("WebService");
+ qt_config->setValue("telemetry_endpoint_url",
+ QString::fromStdString(Settings::values.telemetry_endpoint_url));
+ qt_config->endGroup();
+
qt_config->beginGroup("UI");
qt_config->beginGroup("UILayout");
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 0e4b85a76..4b83eeb28 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -73,7 +73,8 @@ namespace Log {
SUB(Audio, Sink) \
CLS(Input) \
CLS(Network) \
- CLS(Loader)
+ CLS(Loader) \
+ CLS(WebService)
// GetClassName is a macro defined by Windows.h, grrr...
const char* GetLogClassName(Class log_class) {
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index 8f13b80b3..fe4dfed69 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -91,6 +91,7 @@ enum class Class : ClassType {
Loader, ///< ROM loader
Input, ///< Input emulation
Network, ///< Network emulation
+ WebService, ///< Interface to Citra Web Services
Count ///< Total number of logging classes
};
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ea09819e5..b80efe192 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -388,3 +388,6 @@ create_directory_groups(${SRCS} ${HEADERS})
add_library(core STATIC ${SRCS} ${HEADERS})
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt)
+if (ENABLE_WEB_SERVICE)
+ target_link_libraries(core PUBLIC json-headers web_service)
+endif()
diff --git a/src/core/settings.h b/src/core/settings.h
index 03c64c94c..ee16bb90a 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -126,6 +126,9 @@ struct Values {
// Debugging
bool use_gdbstub;
u16 gdbstub_port;
+
+ // WebService
+ std::string telemetry_endpoint_url;
} extern values;
// a special value for Values::region_value indicating that citra will automatically select a region
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index ddc8b262e..70eff4340 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -7,12 +7,18 @@
#include "common/scm_rev.h"
#include "core/telemetry_session.h"
+#ifdef ENABLE_WEB_SERVICE
+#include "web_service/telemetry_json.h"
+#endif
+
namespace Core {
TelemetrySession::TelemetrySession() {
- // TODO(bunnei): Replace with a backend that logs to our web service
+#ifdef ENABLE_WEB_SERVICE
+ backend = std::make_unique<WebService::TelemetryJson>();
+#else
backend = std::make_unique<Telemetry::NullVisitor>();
-
+#endif
// Log one-time session start information
const auto duration{std::chrono::steady_clock::now().time_since_epoch()};
const auto start_time{std::chrono::duration_cast<std::chrono::microseconds>(duration).count()};
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
new file mode 100644
index 000000000..334d82a8a
--- /dev/null
+++ b/src/web_service/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(SRCS
+ telemetry_json.cpp
+ web_backend.cpp
+ )
+
+set(HEADERS
+ telemetry_json.h
+ web_backend.h
+ )
+
+create_directory_groups(${SRCS} ${HEADERS})
+
+add_library(web_service STATIC ${SRCS} ${HEADERS})
+target_link_libraries(web_service PUBLIC common cpr json-headers)
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
new file mode 100644
index 000000000..a2d007e77
--- /dev/null
+++ b/src/web_service/telemetry_json.cpp
@@ -0,0 +1,87 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/settings.h"
+#include "web_service/telemetry_json.h"
+#include "web_service/web_backend.h"
+
+namespace WebService {
+
+template <class T>
+void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) {
+ sections[static_cast<u8>(type)][name] = value;
+}
+
+void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) {
+ TopSection()[name] = sections[static_cast<unsigned>(type)];
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue());
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
+ Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
+}
+
+void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
+ Serialize(field.GetType(), field.GetName(), field.GetValue().count());
+}
+
+void TelemetryJson::Complete() {
+ SerializeSection(Telemetry::FieldType::App, "App");
+ SerializeSection(Telemetry::FieldType::Session, "Session");
+ SerializeSection(Telemetry::FieldType::Performance, "Performance");
+ SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
+ SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
+ SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
+ PostJson(Settings::values.telemetry_endpoint_url, TopSection().dump());
+}
+
+} // namespace WebService
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
new file mode 100644
index 000000000..39038b4f9
--- /dev/null
+++ b/src/web_service/telemetry_json.h
@@ -0,0 +1,54 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include <json.hpp>
+#include "common/telemetry.h"
+
+namespace WebService {
+
+/**
+ * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
+ * Citra web service
+ */
+class TelemetryJson : public Telemetry::VisitorInterface {
+public:
+ TelemetryJson() = default;
+ ~TelemetryJson() = default;
+
+ void Visit(const Telemetry::Field<bool>& field) override;
+ void Visit(const Telemetry::Field<double>& field) override;
+ void Visit(const Telemetry::Field<float>& field) override;
+ void Visit(const Telemetry::Field<u8>& field) override;
+ void Visit(const Telemetry::Field<u16>& field) override;
+ void Visit(const Telemetry::Field<u32>& field) override;
+ void Visit(const Telemetry::Field<u64>& field) override;
+ void Visit(const Telemetry::Field<s8>& field) override;
+ void Visit(const Telemetry::Field<s16>& field) override;
+ void Visit(const Telemetry::Field<s32>& field) override;
+ void Visit(const Telemetry::Field<s64>& field) override;
+ void Visit(const Telemetry::Field<std::string>& field) override;
+ void Visit(const Telemetry::Field<const char*>& field) override;
+ void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
+
+ void Complete() override;
+
+private:
+ nlohmann::json& TopSection() {
+ return sections[static_cast<u8>(Telemetry::FieldType::None)];
+ }
+
+ template <class T>
+ void Serialize(Telemetry::FieldType type, const std::string& name, T value);
+
+ void SerializeSection(Telemetry::FieldType type, const std::string& name);
+
+ nlohmann::json output;
+ std::array<nlohmann::json, 7> sections;
+};
+
+} // namespace WebService
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
new file mode 100644
index 000000000..13e4555ac
--- /dev/null
+++ b/src/web_service/web_backend.cpp
@@ -0,0 +1,52 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cpr/cpr.h>
+#include <stdlib.h>
+#include "common/logging/log.h"
+#include "web_service/web_backend.h"
+
+namespace WebService {
+
+static constexpr char API_VERSION[]{"1"};
+static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"};
+static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"};
+
+static std::string GetEnvironmentVariable(const char* name) {
+ const char* value{getenv(name)};
+ if (value) {
+ return value;
+ }
+ return {};
+}
+
+const std::string& GetUsername() {
+ static const std::string username{GetEnvironmentVariable(ENV_VAR_USERNAME)};
+ return username;
+}
+
+const std::string& GetToken() {
+ static const std::string token{GetEnvironmentVariable(ENV_VAR_TOKEN)};
+ return token;
+}
+
+void PostJson(const std::string& url, const std::string& data) {
+ if (url.empty()) {
+ LOG_ERROR(WebService, "URL is invalid");
+ return;
+ }
+
+ if (GetUsername().empty() || GetToken().empty()) {
+ LOG_ERROR(WebService, "Environment variables %s and %s must be set to POST JSON",
+ ENV_VAR_USERNAME, ENV_VAR_TOKEN);
+ return;
+ }
+
+ cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"},
+ {"x-username", GetUsername()},
+ {"x-token", GetToken()},
+ {"api-version", API_VERSION}});
+}
+
+} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
new file mode 100644
index 000000000..2753d3b68
--- /dev/null
+++ b/src/web_service/web_backend.h
@@ -0,0 +1,31 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include "common/common_types.h"
+
+namespace WebService {
+
+/**
+ * Gets the current username for accessing services.citra-emu.org.
+ * @returns Username as a string, empty if not set.
+ */
+const std::string& GetUsername();
+
+/**
+ * Gets the current token for accessing services.citra-emu.org.
+ * @returns Token as a string, empty if not set.
+ */
+const std::string& GetToken();
+
+/**
+ * Posts JSON to services.citra-emu.org.
+ * @param url URL of the services.citra-emu.org endpoint to post data to.
+ * @param data String of JSON data to use for the body of the POST request.
+ */
+void PostJson(const std::string& url, const std::string& data);
+
+} // namespace WebService