diff options
| author | 2017-07-12 21:31:12 -0400 | |
|---|---|---|
| committer | 2017-07-12 21:31:12 -0400 | |
| commit | 9cf261ba8ba4fd9929d275cc793d48d13df624f3 (patch) | |
| tree | 2eb47ab96bde081a84c5dd7c8107a21cb8a3511c | |
| parent | Merge pull request #2815 from mailwl/bossp (diff) | |
| parent | web_backend: Specify api-version on JSON post. (diff) | |
| download | yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.gz yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.tar.xz yuzu-9cf261ba8ba4fd9929d275cc793d48d13df624f3.zip | |
Merge pull request #2819 from bunnei/telemetry-submit
Telemetry: Submit logged data to the Citra service
| -rw-r--r-- | .gitmodules | 6 | ||||
| -rw-r--r-- | CMakeLists.txt | 5 | ||||
| -rw-r--r-- | externals/CMakeLists.txt | 12 | ||||
| m--------- | externals/cpr | 0 | ||||
| m--------- | externals/json | 0 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/citra/config.cpp | 4 | ||||
| -rw-r--r-- | src/citra/default_ini.h | 4 | ||||
| -rw-r--r-- | src/citra_qt/configuration/config.cpp | 12 | ||||
| -rw-r--r-- | src/common/logging/backend.cpp | 3 | ||||
| -rw-r--r-- | src/common/logging/log.h | 1 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | src/core/settings.h | 3 | ||||
| -rw-r--r-- | src/core/telemetry_session.cpp | 10 | ||||
| -rw-r--r-- | src/web_service/CMakeLists.txt | 14 | ||||
| -rw-r--r-- | src/web_service/telemetry_json.cpp | 87 | ||||
| -rw-r--r-- | src/web_service/telemetry_json.h | 54 | ||||
| -rw-r--r-- | src/web_service/web_backend.cpp | 52 | ||||
| -rw-r--r-- | src/web_service/web_backend.h | 31 |
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 @@ | |||
| 28 | [submodule "externals/enet"] | 28 | [submodule "externals/enet"] |
| 29 | path = externals/enet | 29 | path = externals/enet |
| 30 | url = https://github.com/lsalzman/enet | 30 | url = https://github.com/lsalzman/enet |
| 31 | [submodule "cpr"] | ||
| 32 | path = externals/cpr | ||
| 33 | url = https://github.com/whoshuu/cpr.git | ||
| 34 | [submodule "json"] | ||
| 35 | path = externals/json | ||
| 36 | 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) | |||
| 11 | option(ENABLE_QT "Enable the Qt frontend" ON) | 11 | option(ENABLE_QT "Enable the Qt frontend" ON) |
| 12 | option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) | 12 | option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF) |
| 13 | 13 | ||
| 14 | option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON) | ||
| 15 | |||
| 14 | if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) | 16 | if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) |
| 15 | message(STATUS "Copying pre-commit hook") | 17 | message(STATUS "Copying pre-commit hook") |
| 16 | file(COPY hooks/pre-commit | 18 | file(COPY hooks/pre-commit |
| @@ -223,6 +225,9 @@ if (ENABLE_QT) | |||
| 223 | find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) | 225 | find_package(Qt5 REQUIRED COMPONENTS Widgets OpenGL ${QT_PREFIX_HINT}) |
| 224 | endif() | 226 | endif() |
| 225 | 227 | ||
| 228 | if (ENABLE_WEB_SERVICE) | ||
| 229 | add_definitions(-DENABLE_WEB_SERVICE) | ||
| 230 | endif() | ||
| 226 | 231 | ||
| 227 | # Platform-specific library requirements | 232 | # Platform-specific library requirements |
| 228 | # ====================================== | 233 | # ====================================== |
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() | |||
| 52 | # ENet | 52 | # ENet |
| 53 | add_subdirectory(enet) | 53 | add_subdirectory(enet) |
| 54 | target_include_directories(enet INTERFACE ./enet/include) | 54 | target_include_directories(enet INTERFACE ./enet/include) |
| 55 | |||
| 56 | if (ENABLE_WEB_SERVICE) | ||
| 57 | # CPR | ||
| 58 | option(BUILD_TESTING OFF) | ||
| 59 | option(BUILD_CPR_TESTS OFF) | ||
| 60 | add_subdirectory(cpr) | ||
| 61 | target_include_directories(cpr INTERFACE ./cpr/include) | ||
| 62 | |||
| 63 | # JSON | ||
| 64 | add_library(json-headers INTERFACE) | ||
| 65 | target_include_directories(json-headers INTERFACE ./json/src) | ||
| 66 | 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() | |||
| 14 | if (ENABLE_QT) | 14 | if (ENABLE_QT) |
| 15 | add_subdirectory(citra_qt) | 15 | add_subdirectory(citra_qt) |
| 16 | endif() | 16 | endif() |
| 17 | if (ENABLE_WEB_SERVICE) | ||
| 18 | add_subdirectory(web_service) | ||
| 19 | 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() { | |||
| 151 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); | 151 | Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); |
| 152 | Settings::values.gdbstub_port = | 152 | Settings::values.gdbstub_port = |
| 153 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); | 153 | static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); |
| 154 | |||
| 155 | // Web Service | ||
| 156 | Settings::values.telemetry_endpoint_url = sdl2_config->Get( | ||
| 157 | "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); | ||
| 154 | } | 158 | } |
| 155 | 159 | ||
| 156 | void Config::Reload() { | 160 | 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 | |||
| 168 | # Port for listening to GDB connections. | 168 | # Port for listening to GDB connections. |
| 169 | use_gdbstub=false | 169 | use_gdbstub=false |
| 170 | gdbstub_port=24689 | 170 | gdbstub_port=24689 |
| 171 | |||
| 172 | [WebService] | ||
| 173 | # Endpoint URL for submitting telemetry data | ||
| 174 | telemetry_endpoint_url = | ||
| 171 | )"; | 175 | )"; |
| 172 | } | 176 | } |
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() { | |||
| 133 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); | 133 | Settings::values.gdbstub_port = qt_config->value("gdbstub_port", 24689).toInt(); |
| 134 | qt_config->endGroup(); | 134 | qt_config->endGroup(); |
| 135 | 135 | ||
| 136 | qt_config->beginGroup("WebService"); | ||
| 137 | Settings::values.telemetry_endpoint_url = | ||
| 138 | qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") | ||
| 139 | .toString() | ||
| 140 | .toStdString(); | ||
| 141 | qt_config->endGroup(); | ||
| 142 | |||
| 136 | qt_config->beginGroup("UI"); | 143 | qt_config->beginGroup("UI"); |
| 137 | 144 | ||
| 138 | qt_config->beginGroup("UILayout"); | 145 | qt_config->beginGroup("UILayout"); |
| @@ -268,6 +275,11 @@ void Config::SaveValues() { | |||
| 268 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); | 275 | qt_config->setValue("gdbstub_port", Settings::values.gdbstub_port); |
| 269 | qt_config->endGroup(); | 276 | qt_config->endGroup(); |
| 270 | 277 | ||
| 278 | qt_config->beginGroup("WebService"); | ||
| 279 | qt_config->setValue("telemetry_endpoint_url", | ||
| 280 | QString::fromStdString(Settings::values.telemetry_endpoint_url)); | ||
| 281 | qt_config->endGroup(); | ||
| 282 | |||
| 271 | qt_config->beginGroup("UI"); | 283 | qt_config->beginGroup("UI"); |
| 272 | 284 | ||
| 273 | qt_config->beginGroup("UILayout"); | 285 | 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 { | |||
| 73 | SUB(Audio, Sink) \ | 73 | SUB(Audio, Sink) \ |
| 74 | CLS(Input) \ | 74 | CLS(Input) \ |
| 75 | CLS(Network) \ | 75 | CLS(Network) \ |
| 76 | CLS(Loader) | 76 | CLS(Loader) \ |
| 77 | CLS(WebService) | ||
| 77 | 78 | ||
| 78 | // GetClassName is a macro defined by Windows.h, grrr... | 79 | // GetClassName is a macro defined by Windows.h, grrr... |
| 79 | const char* GetLogClassName(Class log_class) { | 80 | 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 { | |||
| 91 | Loader, ///< ROM loader | 91 | Loader, ///< ROM loader |
| 92 | Input, ///< Input emulation | 92 | Input, ///< Input emulation |
| 93 | Network, ///< Network emulation | 93 | Network, ///< Network emulation |
| 94 | WebService, ///< Interface to Citra Web Services | ||
| 94 | Count ///< Total number of logging classes | 95 | Count ///< Total number of logging classes |
| 95 | }; | 96 | }; |
| 96 | 97 | ||
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}) | |||
| 388 | add_library(core STATIC ${SRCS} ${HEADERS}) | 388 | add_library(core STATIC ${SRCS} ${HEADERS}) |
| 389 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) | 389 | target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) |
| 390 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) | 390 | target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) |
| 391 | if (ENABLE_WEB_SERVICE) | ||
| 392 | target_link_libraries(core PUBLIC json-headers web_service) | ||
| 393 | 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 { | |||
| 126 | // Debugging | 126 | // Debugging |
| 127 | bool use_gdbstub; | 127 | bool use_gdbstub; |
| 128 | u16 gdbstub_port; | 128 | u16 gdbstub_port; |
| 129 | |||
| 130 | // WebService | ||
| 131 | std::string telemetry_endpoint_url; | ||
| 129 | } extern values; | 132 | } extern values; |
| 130 | 133 | ||
| 131 | // a special value for Values::region_value indicating that citra will automatically select a region | 134 | // 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 @@ | |||
| 7 | #include "common/scm_rev.h" | 7 | #include "common/scm_rev.h" |
| 8 | #include "core/telemetry_session.h" | 8 | #include "core/telemetry_session.h" |
| 9 | 9 | ||
| 10 | #ifdef ENABLE_WEB_SERVICE | ||
| 11 | #include "web_service/telemetry_json.h" | ||
| 12 | #endif | ||
| 13 | |||
| 10 | namespace Core { | 14 | namespace Core { |
| 11 | 15 | ||
| 12 | TelemetrySession::TelemetrySession() { | 16 | TelemetrySession::TelemetrySession() { |
| 13 | // TODO(bunnei): Replace with a backend that logs to our web service | 17 | #ifdef ENABLE_WEB_SERVICE |
| 18 | backend = std::make_unique<WebService::TelemetryJson>(); | ||
| 19 | #else | ||
| 14 | backend = std::make_unique<Telemetry::NullVisitor>(); | 20 | backend = std::make_unique<Telemetry::NullVisitor>(); |
| 15 | 21 | #endif | |
| 16 | // Log one-time session start information | 22 | // Log one-time session start information |
| 17 | const auto duration{std::chrono::steady_clock::now().time_since_epoch()}; | 23 | const auto duration{std::chrono::steady_clock::now().time_since_epoch()}; |
| 18 | const auto start_time{std::chrono::duration_cast<std::chrono::microseconds>(duration).count()}; | 24 | 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 @@ | |||
| 1 | set(SRCS | ||
| 2 | telemetry_json.cpp | ||
| 3 | web_backend.cpp | ||
| 4 | ) | ||
| 5 | |||
| 6 | set(HEADERS | ||
| 7 | telemetry_json.h | ||
| 8 | web_backend.h | ||
| 9 | ) | ||
| 10 | |||
| 11 | create_directory_groups(${SRCS} ${HEADERS}) | ||
| 12 | |||
| 13 | add_library(web_service STATIC ${SRCS} ${HEADERS}) | ||
| 14 | 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "core/settings.h" | ||
| 7 | #include "web_service/telemetry_json.h" | ||
| 8 | #include "web_service/web_backend.h" | ||
| 9 | |||
| 10 | namespace WebService { | ||
| 11 | |||
| 12 | template <class T> | ||
| 13 | void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { | ||
| 14 | sections[static_cast<u8>(type)][name] = value; | ||
| 15 | } | ||
| 16 | |||
| 17 | void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { | ||
| 18 | TopSection()[name] = sections[static_cast<unsigned>(type)]; | ||
| 19 | } | ||
| 20 | |||
| 21 | void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { | ||
| 22 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 23 | } | ||
| 24 | |||
| 25 | void TelemetryJson::Visit(const Telemetry::Field<double>& field) { | ||
| 26 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 27 | } | ||
| 28 | |||
| 29 | void TelemetryJson::Visit(const Telemetry::Field<float>& field) { | ||
| 30 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 31 | } | ||
| 32 | |||
| 33 | void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { | ||
| 34 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 35 | } | ||
| 36 | |||
| 37 | void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { | ||
| 38 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 39 | } | ||
| 40 | |||
| 41 | void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { | ||
| 42 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 43 | } | ||
| 44 | |||
| 45 | void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { | ||
| 46 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 47 | } | ||
| 48 | |||
| 49 | void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { | ||
| 50 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 51 | } | ||
| 52 | |||
| 53 | void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { | ||
| 54 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 55 | } | ||
| 56 | |||
| 57 | void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { | ||
| 58 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 59 | } | ||
| 60 | |||
| 61 | void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { | ||
| 62 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 63 | } | ||
| 64 | |||
| 65 | void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { | ||
| 66 | Serialize(field.GetType(), field.GetName(), field.GetValue()); | ||
| 67 | } | ||
| 68 | |||
| 69 | void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { | ||
| 70 | Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); | ||
| 71 | } | ||
| 72 | |||
| 73 | void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { | ||
| 74 | Serialize(field.GetType(), field.GetName(), field.GetValue().count()); | ||
| 75 | } | ||
| 76 | |||
| 77 | void TelemetryJson::Complete() { | ||
| 78 | SerializeSection(Telemetry::FieldType::App, "App"); | ||
| 79 | SerializeSection(Telemetry::FieldType::Session, "Session"); | ||
| 80 | SerializeSection(Telemetry::FieldType::Performance, "Performance"); | ||
| 81 | SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); | ||
| 82 | SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); | ||
| 83 | SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); | ||
| 84 | PostJson(Settings::values.telemetry_endpoint_url, TopSection().dump()); | ||
| 85 | } | ||
| 86 | |||
| 87 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <string> | ||
| 9 | #include <json.hpp> | ||
| 10 | #include "common/telemetry.h" | ||
| 11 | |||
| 12 | namespace WebService { | ||
| 13 | |||
| 14 | /** | ||
| 15 | * Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the | ||
| 16 | * Citra web service | ||
| 17 | */ | ||
| 18 | class TelemetryJson : public Telemetry::VisitorInterface { | ||
| 19 | public: | ||
| 20 | TelemetryJson() = default; | ||
| 21 | ~TelemetryJson() = default; | ||
| 22 | |||
| 23 | void Visit(const Telemetry::Field<bool>& field) override; | ||
| 24 | void Visit(const Telemetry::Field<double>& field) override; | ||
| 25 | void Visit(const Telemetry::Field<float>& field) override; | ||
| 26 | void Visit(const Telemetry::Field<u8>& field) override; | ||
| 27 | void Visit(const Telemetry::Field<u16>& field) override; | ||
| 28 | void Visit(const Telemetry::Field<u32>& field) override; | ||
| 29 | void Visit(const Telemetry::Field<u64>& field) override; | ||
| 30 | void Visit(const Telemetry::Field<s8>& field) override; | ||
| 31 | void Visit(const Telemetry::Field<s16>& field) override; | ||
| 32 | void Visit(const Telemetry::Field<s32>& field) override; | ||
| 33 | void Visit(const Telemetry::Field<s64>& field) override; | ||
| 34 | void Visit(const Telemetry::Field<std::string>& field) override; | ||
| 35 | void Visit(const Telemetry::Field<const char*>& field) override; | ||
| 36 | void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override; | ||
| 37 | |||
| 38 | void Complete() override; | ||
| 39 | |||
| 40 | private: | ||
| 41 | nlohmann::json& TopSection() { | ||
| 42 | return sections[static_cast<u8>(Telemetry::FieldType::None)]; | ||
| 43 | } | ||
| 44 | |||
| 45 | template <class T> | ||
| 46 | void Serialize(Telemetry::FieldType type, const std::string& name, T value); | ||
| 47 | |||
| 48 | void SerializeSection(Telemetry::FieldType type, const std::string& name); | ||
| 49 | |||
| 50 | nlohmann::json output; | ||
| 51 | std::array<nlohmann::json, 7> sections; | ||
| 52 | }; | ||
| 53 | |||
| 54 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cpr/cpr.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "web_service/web_backend.h" | ||
| 9 | |||
| 10 | namespace WebService { | ||
| 11 | |||
| 12 | static constexpr char API_VERSION[]{"1"}; | ||
| 13 | static constexpr char ENV_VAR_USERNAME[]{"CITRA_WEB_SERVICES_USERNAME"}; | ||
| 14 | static constexpr char ENV_VAR_TOKEN[]{"CITRA_WEB_SERVICES_TOKEN"}; | ||
| 15 | |||
| 16 | static std::string GetEnvironmentVariable(const char* name) { | ||
| 17 | const char* value{getenv(name)}; | ||
| 18 | if (value) { | ||
| 19 | return value; | ||
| 20 | } | ||
| 21 | return {}; | ||
| 22 | } | ||
| 23 | |||
| 24 | const std::string& GetUsername() { | ||
| 25 | static const std::string username{GetEnvironmentVariable(ENV_VAR_USERNAME)}; | ||
| 26 | return username; | ||
| 27 | } | ||
| 28 | |||
| 29 | const std::string& GetToken() { | ||
| 30 | static const std::string token{GetEnvironmentVariable(ENV_VAR_TOKEN)}; | ||
| 31 | return token; | ||
| 32 | } | ||
| 33 | |||
| 34 | void PostJson(const std::string& url, const std::string& data) { | ||
| 35 | if (url.empty()) { | ||
| 36 | LOG_ERROR(WebService, "URL is invalid"); | ||
| 37 | return; | ||
| 38 | } | ||
| 39 | |||
| 40 | if (GetUsername().empty() || GetToken().empty()) { | ||
| 41 | LOG_ERROR(WebService, "Environment variables %s and %s must be set to POST JSON", | ||
| 42 | ENV_VAR_USERNAME, ENV_VAR_TOKEN); | ||
| 43 | return; | ||
| 44 | } | ||
| 45 | |||
| 46 | cpr::PostAsync(cpr::Url{url}, cpr::Body{data}, cpr::Header{{"Content-Type", "application/json"}, | ||
| 47 | {"x-username", GetUsername()}, | ||
| 48 | {"x-token", GetToken()}, | ||
| 49 | {"api-version", API_VERSION}}); | ||
| 50 | } | ||
| 51 | |||
| 52 | } // 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 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include "common/common_types.h" | ||
| 9 | |||
| 10 | namespace WebService { | ||
| 11 | |||
| 12 | /** | ||
| 13 | * Gets the current username for accessing services.citra-emu.org. | ||
| 14 | * @returns Username as a string, empty if not set. | ||
| 15 | */ | ||
| 16 | const std::string& GetUsername(); | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Gets the current token for accessing services.citra-emu.org. | ||
| 20 | * @returns Token as a string, empty if not set. | ||
| 21 | */ | ||
| 22 | const std::string& GetToken(); | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Posts JSON to services.citra-emu.org. | ||
| 26 | * @param url URL of the services.citra-emu.org endpoint to post data to. | ||
| 27 | * @param data String of JSON data to use for the body of the POST request. | ||
| 28 | */ | ||
| 29 | void PostJson(const std::string& url, const std::string& data); | ||
| 30 | |||
| 31 | } // namespace WebService | ||